Repository: SVG-Edit/svgedit (language: python)

Issue:
Save images directly to native file system
## Motivation

With the [Native File System API](https://wicg.github.io/file-system-access/) (now implemented in Chromium-based browsers), it is possible to open and save files directly to the native file system. This would allow SVG-edit to have a much better edit-save cycle, like any desktop editor.

## Current behavior

Clicking SVG-edit > Save Image results in a file called "icon (x).svg" downloaded to my computer.
Clicking it again results in a file called "icon (x+1).svg" being downloaded to my computer.

## Desired behavior

Clicking SVG-edit > Save Image should result in the file that I opened natively to be saved to my file system.
Note that for files not originally opened from the native file system, you will have to ask the user where they would like to save the file.

Steps to implement:

1) Start using ```await window.showOpenFilePicker()``` if it's supported by the browser, instead of the standard <input type="file"> mechanism. Remember the file handle.

2) On saving, if a file handle exists, for this image, then upon Save, attempt to create a writable stream from it:

```
        const writableStream = await fileHandle.createWritable();
        writableStream.write(someArrayBuffer);
        writableStream.close();
```

On first save, this will ask the user for permissions for the browser to save the file. Subsequent saves in that session will work just like any other editor.
Save images directly to native file system
## Motivation

With the [Native File System API](https://wicg.github.io/file-system-access/) (now implemented in Chromium-based browsers), it is possible to open and save files directly to the native file system. This would allow SVG-edit to have a much better edit-save cycle, like any desktop editor.

## Current behavior

Clicking SVG-edit > Save Image results in a file called "icon (x).svg" downloaded to my computer.
Clicking it again results in a file called "icon (x+1).svg" being downloaded to my computer.

## Desired behavior

Clicking SVG-edit > Save Image should result in the file that I opened natively to be saved to my file system.
Note that for files not originally opened from the native file system, you will have to ask the user where they would like to save the file.

Steps to implement:

1) Start using ```await window.showOpenFilePicker()``` if it's supported by the browser, instead of the standard <input type="file"> mechanism. Remember the file handle.

2) On saving, if a file handle exists, for this image, then upon Save, attempt to create a writable stream from it:

```
        const writableStream = await fileHandle.createWritable();
        writableStream.write(someArrayBuffer);
        writableStream.close();
```

On first save, this will ask the user for permissions for the browser to save the file. Subsequent saves in that session will work just like any other editor.
Save images directly to native file system
## Motivation

With the [Native File System API](https://wicg.github.io/file-system-access/) (now implemented in Chromium-based browsers), it is possible to open and save files directly to the native file system. This would allow SVG-edit to have a much better edit-save cycle, like any desktop editor.

## Current behavior

Clicking SVG-edit > Save Image results in a file called "icon (x).svg" downloaded to my computer.
Clicking it again results in a file called "icon (x+1).svg" being downloaded to my computer.

## Desired behavior

Clicking SVG-edit > Save Image should result in the file that I opened natively to be saved to my file system.
Note that for files not originally opened from the native file system, you will have to ask the user where they would like to save the file.

Steps to implement:

1) Start using ```await window.showOpenFilePicker()``` if it's supported by the browser, instead of the standard <input type="file"> mechanism. Remember the file handle.

2) On saving, if a file handle exists, for this image, then upon Save, attempt to create a writable stream from it:

```
        const writableStream = await fileHandle.createWritable();
        writableStream.write(someArrayBuffer);
        writableStream.close();
```

On first save, this will ask the user for permissions for the browser to save the file. Subsequent saves in that session will work just like any other editor.
Save images directly to native file system
## Motivation

With the [Native File System API](https://wicg.github.io/file-system-access/) (now implemented in Chromium-based browsers), it is possible to open and save files directly to the native file system. This would allow SVG-edit to have a much better edit-save cycle, like any desktop editor.

## Current behavior

Clicking SVG-edit > Save Image results in a file called "icon (x).svg" downloaded to my computer.
Clicking it again results in a file called "icon (x+1).svg" being downloaded to my computer.

## Desired behavior

Clicking SVG-edit > Save Image should result in the file that I opened natively to be saved to my file system.
Note that for files not originally opened from the native file system, you will have to ask the user where they would like to save the file.

Steps to implement:

1) Start using ```await window.showOpenFilePicker()``` if it's supported by the browser, instead of the standard <input type="file"> mechanism. Remember the file handle.

2) On saving, if a file handle exists, for this image, then upon Save, attempt to create a writable stream from it:

```
        const writableStream = await fileHandle.createWritable();
        writableStream.write(someArrayBuffer);
        writableStream.close();
```

On first save, this will ask the user for permissions for the browser to save the file. Subsequent saves in that session will work just like any other editor.
Save images directly to native file system
## Motivation

With the [Native File System API](https://wicg.github.io/file-system-access/) (now implemented in Chromium-based browsers), it is possible to open and save files directly to the native file system. This would allow SVG-edit to have a much better edit-save cycle, like any desktop editor.

## Current behavior

Clicking SVG-edit > Save Image results in a file called "icon (x).svg" downloaded to my computer.
Clicking it again results in a file called "icon (x+1).svg" being downloaded to my computer.

## Desired behavior

Clicking SVG-edit > Save Image should result in the file that I opened natively to be saved to my file system.
Note that for files not originally opened from the native file system, you will have to ask the user where they would like to save the file.

Steps to implement:

1) Start using ```await window.showOpenFilePicker()``` if it's supported by the browser, instead of the standard <input type="file"> mechanism. Remember the file handle.

2) On saving, if a file handle exists, for this image, then upon Save, attempt to create a writable stream from it:

```
        const writableStream = await fileHandle.createWritable();
        writableStream.write(someArrayBuffer);
        writableStream.close();
```

On first save, this will ask the user for permissions for the browser to save the file. Subsequent saves in that session will work just like any other editor.
Save images directly to native file system
## Motivation

With the [Native File System API](https://wicg.github.io/file-system-access/) (now implemented in Chromium-based browsers), it is possible to open and save files directly to the native file system. This would allow SVG-edit to have a much better edit-save cycle, like any desktop editor.

## Current behavior

Clicking SVG-edit > Save Image results in a file called "icon (x).svg" downloaded to my computer.
Clicking it again results in a file called "icon (x+1).svg" being downloaded to my computer.

## Desired behavior

Clicking SVG-edit > Save Image should result in the file that I opened natively to be saved to my file system.
Note that for files not originally opened from the native file system, you will have to ask the user where they would like to save the file.

Steps to implement:

1) Start using ```await window.showOpenFilePicker()``` if it's supported by the browser, instead of the standard <input type="file"> mechanism. Remember the file handle.

2) On saving, if a file handle exists, for this image, then upon Save, attempt to create a writable stream from it:

```
        const writableStream = await fileHandle.createWritable();
        writableStream.write(someArrayBuffer);
        writableStream.close();
```

On first save, this will ask the user for permissions for the browser to save the file. Subsequent saves in that session will work just like any other editor.
Save images directly to native file system
## Motivation

With the [Native File System API](https://wicg.github.io/file-system-access/) (now implemented in Chromium-based browsers), it is possible to open and save files directly to the native file system. This would allow SVG-edit to have a much better edit-save cycle, like any desktop editor.

## Current behavior

Clicking SVG-edit > Save Image results in a file called "icon (x).svg" downloaded to my computer.
Clicking it again results in a file called "icon (x+1).svg" being downloaded to my computer.

## Desired behavior

Clicking SVG-edit > Save Image should result in the file that I opened natively to be saved to my file system.
Note that for files not originally opened from the native file system, you will have to ask the user where they would like to save the file.

Steps to implement:

1) Start using ```await window.showOpenFilePicker()``` if it's supported by the browser, instead of the standard <input type="file"> mechanism. Remember the file handle.

2) On saving, if a file handle exists, for this image, then upon Save, attempt to create a writable stream from it:

```
        const writableStream = await fileHandle.createWritable();
        writableStream.write(someArrayBuffer);
        writableStream.close();
```

On first save, this will ask the user for permissions for the browser to save the file. Subsequent saves in that session will work just like any other editor.
Save images directly to native file system
## Motivation

With the [Native File System API](https://wicg.github.io/file-system-access/) (now implemented in Chromium-based browsers), it is possible to open and save files directly to the native file system. This would allow SVG-edit to have a much better edit-save cycle, like any desktop editor.

## Current behavior

Clicking SVG-edit > Save Image results in a file called "icon (x).svg" downloaded to my computer.
Clicking it again results in a file called "icon (x+1).svg" being downloaded to my computer.

## Desired behavior

Clicking SVG-edit > Save Image should result in the file that I opened natively to be saved to my file system.
Note that for files not originally opened from the native file system, you will have to ask the user where they would like to save the file.

Steps to implement:

1) Start using ```await window.showOpenFilePicker()``` if it's supported by the browser, instead of the standard <input type="file"> mechanism. Remember the file handle.

2) On saving, if a file handle exists, for this image, then upon Save, attempt to create a writable stream from it:

```
        const writableStream = await fileHandle.createWritable();
        writableStream.write(someArrayBuffer);
        writableStream.close();
```

On first save, this will ask the user for permissions for the browser to save the file. Subsequent saves in that session will work just like any other editor.
Save images directly to native file system
## Motivation

With the [Native File System API](https://wicg.github.io/file-system-access/) (now implemented in Chromium-based browsers), it is possible to open and save files directly to the native file system. This would allow SVG-edit to have a much better edit-save cycle, like any desktop editor.

## Current behavior

Clicking SVG-edit > Save Image results in a file called "icon (x).svg" downloaded to my computer.
Clicking it again results in a file called "icon (x+1).svg" being downloaded to my computer.

## Desired behavior

Clicking SVG-edit > Save Image should result in the file that I opened natively to be saved to my file system.
Note that for files not originally opened from the native file system, you will have to ask the user where they would like to save the file.

Steps to implement:

1) Start using ```await window.showOpenFilePicker()``` if it's supported by the browser, instead of the standard <input type="file"> mechanism. Remember the file handle.

2) On saving, if a file handle exists, for this image, then upon Save, attempt to create a writable stream from it:

```
        const writableStream = await fileHandle.createWritable();
        writableStream.write(someArrayBuffer);
        writableStream.close();
```

On first save, this will ask the user for permissions for the browser to save the file. Subsequent saves in that session will work just like any other editor.


Discussion / hints:
@codedread I did a quick test in the branch https://github.com/SVG-Edit/svgedit/tree/test-native-file-api
This is working very well. 
Things to watch:
- When you refresh the page, the filehandle is lost (needs to be saved) 
- We need a "save as..." option to be consistent
- Safari does not support this API for now (but we could use https://github.com/GoogleChromeLabs/browser-fs-access that proposes a fallback when the API is not supported). 

Thank you for the suggestion. I wish to include this in V7 definitely.

You can try the branch on https://6103c9c465f45d0008665207--svgedit.netlify.app/editor/index.html
I tested this out... Very cool!  Couple notes:

- Now that this works, maybe it is not a great idea to bring up the last-saved image. Blank canvas upon refresh now?
- Yes, I think Save As needs to be there, as well as Save working like Save As for a new image created from scratch. I think [showSaveFilePicker()](https://wicg.github.io/file-system-access/#api-showsavefilepicker) is your friend (I haven't used this myself in my own projects yet).
- In terms of other browsers not supporting this, I think the feature needs to be implemented with "progressive enhancement" in mind - browsers will be incentivized to implement this API if more web developers show an interest in it.
- This API also comes with a [showDirectoryPicker()](https://wicg.github.io/file-system-access/#api-showdirectorypicker) which gives the web app access to all files/sub-directories in that directory - not sure what to do with that for SVG-Edit, but something to keep in mind. :-). ... Like, maybe you can choose a "Library" folder for SVG-Edit and all files are saved/accessed from within the web app?


In uploading and downloading audio data on one of my pages, I have used the InexedDB for storage and refresh. (this is actually a vanilla component that is lazy loaded onto a page.) {Also, some pages still being pulled back out after taking stuff down form last summer if you happen to find my website. www.copious.world} 

So, once the audio is save and wave forms have been edited, data is in the IndexedDB. From there I can just use a data URL. I believe that is an old way. The new API sounds promising. 

But, the components can just grab state from the DB. 
Another thing I have done recently is to store files in IPFS. So, far I have used a web server to accept data. IPFS in the browser has been tricky. I am not sure they got it just right. They require WebRTC signalling. But, I think that has to do with having the browser be a full node. 

I believe there is progress that can be made in just preparing hashes and storage formats and using a peer swam based on websockets, which might also serve MQTT. The swarm can forward content, and the browser can manage some storage for the swarm.  But even with huge space now taken by browsers for local storage, having the browser dedicate P2P space is a little tough on resources.
We will look into the native file system approach in V7. I think we will make it our standard approach and keep the old way as a plugin to be used for old browsers. However, I want also to test out the GoogleChromeLabs/browser-fs-access library if this helps to include Safari in the supported browsers. 
More and more, we will use modern approach in svgedit that may limit old browser support but offer a much better user experience. Obviously, we will keep older version of svgedit available for old browsers.
@codedread I did a quick test in the branch https://github.com/SVG-Edit/svgedit/tree/test-native-file-api
This is working very well. 
Things to watch:
- When you refresh the page, the filehandle is lost (needs to be saved) 
- We need a "save as..." option to be consistent
- Safari does not support this API for now (but we could use https://github.com/GoogleChromeLabs/browser-fs-access that proposes a fallback when the API is not supported). 

Thank you for the suggestion. I wish to include this in V7 definitely.

You can try the branch on https://6103c9c465f45d0008665207--svgedit.netlify.app/editor/index.html
I tested this out... Very cool!  Couple notes:

- Now that this works, maybe it is not a great idea to bring up the last-saved image. Blank canvas upon refresh now?
- Yes, I think Save As needs to be there, as well as Save working like Save As for a new image created from scratch. I think [showSaveFilePicker()](https://wicg.github.io/file-system-access/#api-showsavefilepicker) is your friend (I haven't used this myself in my own projects yet).
- In terms of other browsers not supporting this, I think the feature needs to be implemented with "progressive enhancement" in mind - browsers will be incentivized to implement this API if more web developers show an interest in it.
- This API also comes with a [showDirectoryPicker()](https://wicg.github.io/file-system-access/#api-showdirectorypicker) which gives the web app access to all files/sub-directories in that directory - not sure what to do with that for SVG-Edit, but something to keep in mind. :-). ... Like, maybe you can choose a "Library" folder for SVG-Edit and all files are saved/accessed from within the web app?


In uploading and downloading audio data on one of my pages, I have used the InexedDB for storage and refresh. (this is actually a vanilla component that is lazy loaded onto a page.) {Also, some pages still being pulled back out after taking stuff down form last summer if you happen to find my website. www.copious.world} 

So, once the audio is save and wave forms have been edited, data is in the IndexedDB. From there I can just use a data URL. I believe that is an old way. The new API sounds promising. 

But, the components can just grab state from the DB. 
Another thing I have done recently is to store files in IPFS. So, far I have used a web server to accept data. IPFS in the browser has been tricky. I am not sure they got it just right. They require WebRTC signalling. But, I think that has to do with having the browser be a full node. 

I believe there is progress that can be made in just preparing hashes and storage formats and using a peer swam based on websockets, which might also serve MQTT. The swarm can forward content, and the browser can manage some storage for the swarm.  But even with huge space now taken by browsers for local storage, having the browser dedicate P2P space is a little tough on resources.
We will look into the native file system approach in V7. I think we will make it our standard approach and keep the old way as a plugin to be used for old browsers. However, I want also to test out the GoogleChromeLabs/browser-fs-access library if this helps to include Safari in the supported browsers. 
More and more, we will use modern approach in svgedit that may limit old browser support but offer a much better user experience. Obviously, we will keep older version of svgedit available for old browsers.
@codedread I did a quick test in the branch https://github.com/SVG-Edit/svgedit/tree/test-native-file-api
This is working very well. 
Things to watch:
- When you refresh the page, the filehandle is lost (needs to be saved) 
- We need a "save as..." option to be consistent
- Safari does not support this API for now (but we could use https://github.com/GoogleChromeLabs/browser-fs-access that proposes a fallback when the API is not supported). 

Thank you for the suggestion. I wish to include this in V7 definitely.

You can try the branch on https://6103c9c465f45d0008665207--svgedit.netlify.app/editor/index.html
I tested this out... Very cool!  Couple notes:

- Now that this works, maybe it is not a great idea to bring up the last-saved image. Blank canvas upon refresh now?
- Yes, I think Save As needs to be there, as well as Save working like Save As for a new image created from scratch. I think [showSaveFilePicker()](https://wicg.github.io/file-system-access/#api-showsavefilepicker) is your friend (I haven't used this myself in my own projects yet).
- In terms of other browsers not supporting this, I think the feature needs to be implemented with "progressive enhancement" in mind - browsers will be incentivized to implement this API if more web developers show an interest in it.
- This API also comes with a [showDirectoryPicker()](https://wicg.github.io/file-system-access/#api-showdirectorypicker) which gives the web app access to all files/sub-directories in that directory - not sure what to do with that for SVG-Edit, but something to keep in mind. :-). ... Like, maybe you can choose a "Library" folder for SVG-Edit and all files are saved/accessed from within the web app?


In uploading and downloading audio data on one of my pages, I have used the InexedDB for storage and refresh. (this is actually a vanilla component that is lazy loaded onto a page.) {Also, some pages still being pulled back out after taking stuff down form last summer if you happen to find my website. www.copious.world} 

So, once the audio is save and wave forms have been edited, data is in the IndexedDB. From there I can just use a data URL. I believe that is an old way. The new API sounds promising. 

But, the components can just grab state from the DB. 
Another thing I have done recently is to store files in IPFS. So, far I have used a web server to accept data. IPFS in the browser has been tricky. I am not sure they got it just right. They require WebRTC signalling. But, I think that has to do with having the browser be a full node. 

I believe there is progress that can be made in just preparing hashes and storage formats and using a peer swam based on websockets, which might also serve MQTT. The swarm can forward content, and the browser can manage some storage for the swarm.  But even with huge space now taken by browsers for local storage, having the browser dedicate P2P space is a little tough on resources.
We will look into the native file system approach in V7. I think we will make it our standard approach and keep the old way as a plugin to be used for old browsers. However, I want also to test out the GoogleChromeLabs/browser-fs-access library if this helps to include Safari in the supported browsers. 
More and more, we will use modern approach in svgedit that may limit old browser support but offer a much better user experience. Obviously, we will keep older version of svgedit available for old browsers.
@codedread I did a quick test in the branch https://github.com/SVG-Edit/svgedit/tree/test-native-file-api
This is working very well. 
Things to watch:
- When you refresh the page, the filehandle is lost (needs to be saved) 
- We need a "save as..." option to be consistent
- Safari does not support this API for now (but we could use https://github.com/GoogleChromeLabs/browser-fs-access that proposes a fallback when the API is not supported). 

Thank you for the suggestion. I wish to include this in V7 definitely.

You can try the branch on https://6103c9c465f45d0008665207--svgedit.netlify.app/editor/index.html
I tested this out... Very cool!  Couple notes:

- Now that this works, maybe it is not a great idea to bring up the last-saved image. Blank canvas upon refresh now?
- Yes, I think Save As needs to be there, as well as Save working like Save As for a new image created from scratch. I think [showSaveFilePicker()](https://wicg.github.io/file-system-access/#api-showsavefilepicker) is your friend (I haven't used this myself in my own projects yet).
- In terms of other browsers not supporting this, I think the feature needs to be implemented with "progressive enhancement" in mind - browsers will be incentivized to implement this API if more web developers show an interest in it.
- This API also comes with a [showDirectoryPicker()](https://wicg.github.io/file-system-access/#api-showdirectorypicker) which gives the web app access to all files/sub-directories in that directory - not sure what to do with that for SVG-Edit, but something to keep in mind. :-). ... Like, maybe you can choose a "Library" folder for SVG-Edit and all files are saved/accessed from within the web app?


In uploading and downloading audio data on one of my pages, I have used the InexedDB for storage and refresh. (this is actually a vanilla component that is lazy loaded onto a page.) {Also, some pages still being pulled back out after taking stuff down form last summer if you happen to find my website. www.copious.world} 

So, once the audio is save and wave forms have been edited, data is in the IndexedDB. From there I can just use a data URL. I believe that is an old way. The new API sounds promising. 

But, the components can just grab state from the DB. 
Another thing I have done recently is to store files in IPFS. So, far I have used a web server to accept data. IPFS in the browser has been tricky. I am not sure they got it just right. They require WebRTC signalling. But, I think that has to do with having the browser be a full node. 

I believe there is progress that can be made in just preparing hashes and storage formats and using a peer swam based on websockets, which might also serve MQTT. The swarm can forward content, and the browser can manage some storage for the swarm.  But even with huge space now taken by browsers for local storage, having the browser dedicate P2P space is a little tough on resources.
We will look into the native file system approach in V7. I think we will make it our standard approach and keep the old way as a plugin to be used for old browsers. However, I want also to test out the GoogleChromeLabs/browser-fs-access library if this helps to include Safari in the supported browsers. 
More and more, we will use modern approach in svgedit that may limit old browser support but offer a much better user experience. Obviously, we will keep older version of svgedit available for old browsers.
@codedread I did a quick test in the branch https://github.com/SVG-Edit/svgedit/tree/test-native-file-api
This is working very well. 
Things to watch:
- When you refresh the page, the filehandle is lost (needs to be saved) 
- We need a "save as..." option to be consistent
- Safari does not support this API for now (but we could use https://github.com/GoogleChromeLabs/browser-fs-access that proposes a fallback when the API is not supported). 

Thank you for the suggestion. I wish to include this in V7 definitely.

You can try the branch on https://6103c9c465f45d0008665207--svgedit.netlify.app/editor/index.html
I tested this out... Very cool!  Couple notes:

- Now that this works, maybe it is not a great idea to bring up the last-saved image. Blank canvas upon refresh now?
- Yes, I think Save As needs to be there, as well as Save working like Save As for a new image created from scratch. I think [showSaveFilePicker()](https://wicg.github.io/file-system-access/#api-showsavefilepicker) is your friend (I haven't used this myself in my own projects yet).
- In terms of other browsers not supporting this, I think the feature needs to be implemented with "progressive enhancement" in mind - browsers will be incentivized to implement this API if more web developers show an interest in it.
- This API also comes with a [showDirectoryPicker()](https://wicg.github.io/file-system-access/#api-showdirectorypicker) which gives the web app access to all files/sub-directories in that directory - not sure what to do with that for SVG-Edit, but something to keep in mind. :-). ... Like, maybe you can choose a "Library" folder for SVG-Edit and all files are saved/accessed from within the web app?


In uploading and downloading audio data on one of my pages, I have used the InexedDB for storage and refresh. (this is actually a vanilla component that is lazy loaded onto a page.) {Also, some pages still being pulled back out after taking stuff down form last summer if you happen to find my website. www.copious.world} 

So, once the audio is save and wave forms have been edited, data is in the IndexedDB. From there I can just use a data URL. I believe that is an old way. The new API sounds promising. 

But, the components can just grab state from the DB. 
Another thing I have done recently is to store files in IPFS. So, far I have used a web server to accept data. IPFS in the browser has been tricky. I am not sure they got it just right. They require WebRTC signalling. But, I think that has to do with having the browser be a full node. 

I believe there is progress that can be made in just preparing hashes and storage formats and using a peer swam based on websockets, which might also serve MQTT. The swarm can forward content, and the browser can manage some storage for the swarm.  But even with huge space now taken by browsers for local storage, having the browser dedicate P2P space is a little tough on resources.
We will look into the native file system approach in V7. I think we will make it our standard approach and keep the old way as a plugin to be used for old browsers. However, I want also to test out the GoogleChromeLabs/browser-fs-access library if this helps to include Safari in the supported browsers. 
More and more, we will use modern approach in svgedit that may limit old browser support but offer a much better user experience. Obviously, we will keep older version of svgedit available for old browsers.
@codedread I did a quick test in the branch https://github.com/SVG-Edit/svgedit/tree/test-native-file-api
This is working very well. 
Things to watch:
- When you refresh the page, the filehandle is lost (needs to be saved) 
- We need a "save as..." option to be consistent
- Safari does not support this API for now (but we could use https://github.com/GoogleChromeLabs/browser-fs-access that proposes a fallback when the API is not supported). 

Thank you for the suggestion. I wish to include this in V7 definitely.

You can try the branch on https://6103c9c465f45d0008665207--svgedit.netlify.app/editor/index.html
I tested this out... Very cool!  Couple notes:

- Now that this works, maybe it is not a great idea to bring up the last-saved image. Blank canvas upon refresh now?
- Yes, I think Save As needs to be there, as well as Save working like Save As for a new image created from scratch. I think [showSaveFilePicker()](https://wicg.github.io/file-system-access/#api-showsavefilepicker) is your friend (I haven't used this myself in my own projects yet).
- In terms of other browsers not supporting this, I think the feature needs to be implemented with "progressive enhancement" in mind - browsers will be incentivized to implement this API if more web developers show an interest in it.
- This API also comes with a [showDirectoryPicker()](https://wicg.github.io/file-system-access/#api-showdirectorypicker) which gives the web app access to all files/sub-directories in that directory - not sure what to do with that for SVG-Edit, but something to keep in mind. :-). ... Like, maybe you can choose a "Library" folder for SVG-Edit and all files are saved/accessed from within the web app?


In uploading and downloading audio data on one of my pages, I have used the InexedDB for storage and refresh. (this is actually a vanilla component that is lazy loaded onto a page.) {Also, some pages still being pulled back out after taking stuff down form last summer if you happen to find my website. www.copious.world} 

So, once the audio is save and wave forms have been edited, data is in the IndexedDB. From there I can just use a data URL. I believe that is an old way. The new API sounds promising. 

But, the components can just grab state from the DB. 
Another thing I have done recently is to store files in IPFS. So, far I have used a web server to accept data. IPFS in the browser has been tricky. I am not sure they got it just right. They require WebRTC signalling. But, I think that has to do with having the browser be a full node. 

I believe there is progress that can be made in just preparing hashes and storage formats and using a peer swam based on websockets, which might also serve MQTT. The swarm can forward content, and the browser can manage some storage for the swarm.  But even with huge space now taken by browsers for local storage, having the browser dedicate P2P space is a little tough on resources.
We will look into the native file system approach in V7. I think we will make it our standard approach and keep the old way as a plugin to be used for old browsers. However, I want also to test out the GoogleChromeLabs/browser-fs-access library if this helps to include Safari in the supported browsers. 
More and more, we will use modern approach in svgedit that may limit old browser support but offer a much better user experience. Obviously, we will keep older version of svgedit available for old browsers.
@codedread I did a quick test in the branch https://github.com/SVG-Edit/svgedit/tree/test-native-file-api
This is working very well. 
Things to watch:
- When you refresh the page, the filehandle is lost (needs to be saved) 
- We need a "save as..." option to be consistent
- Safari does not support this API for now (but we could use https://github.com/GoogleChromeLabs/browser-fs-access that proposes a fallback when the API is not supported). 

Thank you for the suggestion. I wish to include this in V7 definitely.

You can try the branch on https://6103c9c465f45d0008665207--svgedit.netlify.app/editor/index.html
I tested this out... Very cool!  Couple notes:

- Now that this works, maybe it is not a great idea to bring up the last-saved image. Blank canvas upon refresh now?
- Yes, I think Save As needs to be there, as well as Save working like Save As for a new image created from scratch. I think [showSaveFilePicker()](https://wicg.github.io/file-system-access/#api-showsavefilepicker) is your friend (I haven't used this myself in my own projects yet).
- In terms of other browsers not supporting this, I think the feature needs to be implemented with "progressive enhancement" in mind - browsers will be incentivized to implement this API if more web developers show an interest in it.
- This API also comes with a [showDirectoryPicker()](https://wicg.github.io/file-system-access/#api-showdirectorypicker) which gives the web app access to all files/sub-directories in that directory - not sure what to do with that for SVG-Edit, but something to keep in mind. :-). ... Like, maybe you can choose a "Library" folder for SVG-Edit and all files are saved/accessed from within the web app?


In uploading and downloading audio data on one of my pages, I have used the InexedDB for storage and refresh. (this is actually a vanilla component that is lazy loaded onto a page.) {Also, some pages still being pulled back out after taking stuff down form last summer if you happen to find my website. www.copious.world} 

So, once the audio is save and wave forms have been edited, data is in the IndexedDB. From there I can just use a data URL. I believe that is an old way. The new API sounds promising. 

But, the components can just grab state from the DB. 
Another thing I have done recently is to store files in IPFS. So, far I have used a web server to accept data. IPFS in the browser has been tricky. I am not sure they got it just right. They require WebRTC signalling. But, I think that has to do with having the browser be a full node. 

I believe there is progress that can be made in just preparing hashes and storage formats and using a peer swam based on websockets, which might also serve MQTT. The swarm can forward content, and the browser can manage some storage for the swarm.  But even with huge space now taken by browsers for local storage, having the browser dedicate P2P space is a little tough on resources.
We will look into the native file system approach in V7. I think we will make it our standard approach and keep the old way as a plugin to be used for old browsers. However, I want also to test out the GoogleChromeLabs/browser-fs-access library if this helps to include Safari in the supported browsers. 
More and more, we will use modern approach in svgedit that may limit old browser support but offer a much better user experience. Obviously, we will keep older version of svgedit available for old browsers.
@codedread I did a quick test in the branch https://github.com/SVG-Edit/svgedit/tree/test-native-file-api
This is working very well. 
Things to watch:
- When you refresh the page, the filehandle is lost (needs to be saved) 
- We need a "save as..." option to be consistent
- Safari does not support this API for now (but we could use https://github.com/GoogleChromeLabs/browser-fs-access that proposes a fallback when the API is not supported). 

Thank you for the suggestion. I wish to include this in V7 definitely.

You can try the branch on https://6103c9c465f45d0008665207--svgedit.netlify.app/editor/index.html
I tested this out... Very cool!  Couple notes:

- Now that this works, maybe it is not a great idea to bring up the last-saved image. Blank canvas upon refresh now?
- Yes, I think Save As needs to be there, as well as Save working like Save As for a new image created from scratch. I think [showSaveFilePicker()](https://wicg.github.io/file-system-access/#api-showsavefilepicker) is your friend (I haven't used this myself in my own projects yet).
- In terms of other browsers not supporting this, I think the feature needs to be implemented with "progressive enhancement" in mind - browsers will be incentivized to implement this API if more web developers show an interest in it.
- This API also comes with a [showDirectoryPicker()](https://wicg.github.io/file-system-access/#api-showdirectorypicker) which gives the web app access to all files/sub-directories in that directory - not sure what to do with that for SVG-Edit, but something to keep in mind. :-). ... Like, maybe you can choose a "Library" folder for SVG-Edit and all files are saved/accessed from within the web app?


In uploading and downloading audio data on one of my pages, I have used the InexedDB for storage and refresh. (this is actually a vanilla component that is lazy loaded onto a page.) {Also, some pages still being pulled back out after taking stuff down form last summer if you happen to find my website. www.copious.world} 

So, once the audio is save and wave forms have been edited, data is in the IndexedDB. From there I can just use a data URL. I believe that is an old way. The new API sounds promising. 

But, the components can just grab state from the DB. 
Another thing I have done recently is to store files in IPFS. So, far I have used a web server to accept data. IPFS in the browser has been tricky. I am not sure they got it just right. They require WebRTC signalling. But, I think that has to do with having the browser be a full node. 

I believe there is progress that can be made in just preparing hashes and storage formats and using a peer swam based on websockets, which might also serve MQTT. The swarm can forward content, and the browser can manage some storage for the swarm.  But even with huge space now taken by browsers for local storage, having the browser dedicate P2P space is a little tough on resources.
We will look into the native file system approach in V7. I think we will make it our standard approach and keep the old way as a plugin to be used for old browsers. However, I want also to test out the GoogleChromeLabs/browser-fs-access library if this helps to include Safari in the supported browsers. 
More and more, we will use modern approach in svgedit that may limit old browser support but offer a much better user experience. Obviously, we will keep older version of svgedit available for old browsers.
@codedread I did a quick test in the branch https://github.com/SVG-Edit/svgedit/tree/test-native-file-api
This is working very well. 
Things to watch:
- When you refresh the page, the filehandle is lost (needs to be saved) 
- We need a "save as..." option to be consistent
- Safari does not support this API for now (but we could use https://github.com/GoogleChromeLabs/browser-fs-access that proposes a fallback when the API is not supported). 

Thank you for the suggestion. I wish to include this in V7 definitely.

You can try the branch on https://6103c9c465f45d0008665207--svgedit.netlify.app/editor/index.html
I tested this out... Very cool!  Couple notes:

- Now that this works, maybe it is not a great idea to bring up the last-saved image. Blank canvas upon refresh now?
- Yes, I think Save As needs to be there, as well as Save working like Save As for a new image created from scratch. I think [showSaveFilePicker()](https://wicg.github.io/file-system-access/#api-showsavefilepicker) is your friend (I haven't used this myself in my own projects yet).
- In terms of other browsers not supporting this, I think the feature needs to be implemented with "progressive enhancement" in mind - browsers will be incentivized to implement this API if more web developers show an interest in it.
- This API also comes with a [showDirectoryPicker()](https://wicg.github.io/file-system-access/#api-showdirectorypicker) which gives the web app access to all files/sub-directories in that directory - not sure what to do with that for SVG-Edit, but something to keep in mind. :-). ... Like, maybe you can choose a "Library" folder for SVG-Edit and all files are saved/accessed from within the web app?


In uploading and downloading audio data on one of my pages, I have used the InexedDB for storage and refresh. (this is actually a vanilla component that is lazy loaded onto a page.) {Also, some pages still being pulled back out after taking stuff down form last summer if you happen to find my website. www.copious.world} 

So, once the audio is save and wave forms have been edited, data is in the IndexedDB. From there I can just use a data URL. I believe that is an old way. The new API sounds promising. 

But, the components can just grab state from the DB. 
Another thing I have done recently is to store files in IPFS. So, far I have used a web server to accept data. IPFS in the browser has been tricky. I am not sure they got it just right. They require WebRTC signalling. But, I think that has to do with having the browser be a full node. 

I believe there is progress that can be made in just preparing hashes and storage formats and using a peer swam based on websockets, which might also serve MQTT. The swarm can forward content, and the browser can manage some storage for the swarm.  But even with huge space now taken by browsers for local storage, having the browser dedicate P2P space is a little tough on resources.
We will look into the native file system approach in V7. I think we will make it our standard approach and keep the old way as a plugin to be used for old browsers. However, I want also to test out the GoogleChromeLabs/browser-fs-access library if this helps to include Safari in the supported browsers. 
More and more, we will use modern approach in svgedit that may limit old browser support but offer a much better user experience. Obviously, we will keep older version of svgedit available for old browsers.

Fix patch:
diff --git a/archive/extensions/ext-opensave/ext-opensave.js b/archive/extensions/ext-opensave/ext-opensave.js
new file mode 100644
index 000000000..b856d2fec
--- /dev/null
+++ b/archive/extensions/ext-opensave/ext-opensave.js
@@ -0,0 +1,78 @@
+/* globals seAlert */
+/**
+ * @file ext-opensave.js
+ *
+ * @license MIT
+ *
+ * @copyright 2020 OptimistikSAS
+ *
+ */
+
+/**
+   * @type {module:svgcanvas.EventHandler}
+   * @param {external:Window} wind
+   * @param {module:svgcanvas.SvgCanvas#event:saved} svg The SVG source
+   * @listens module:svgcanvas.SvgCanvas#event:saved
+   * @returns {void}
+   */
+
+export default {
+  name: 'opensave',
+  init ({ encode64 }) {
+    const svgEditor = this;
+
+    svgEditor.setCustomHandlers({
+      save (win, svg) {
+        this.showSaveWarning = false;
+
+        // by default, we add the XML prolog back, systems integrating SVG-edit (wikis, CMSs)
+        // can just provide their own custom save handler and might not want the XML prolog
+        svg = '<?xml version="1.0"?>\n' + svg;
+
+        // Since saving SVGs by opening a new window was removed in Chrome use artificial link-click
+        // https://stackoverflow.com/questions/45603201/window-is-not-allowed-to-navigate-top-frame-navigations-to-data-urls
+        const a = document.createElement('a');
+        a.href = 'data:image/svg+xml;base64,' + encode64(svg);
+        a.download = 'icon.svg';
+        a.style.display = 'none';
+        document.body.append(a); // Need to append for Firefox
+
+        a.click();
+
+        // Alert will only appear the first time saved OR the
+        //   first time the bug is encountered
+        const done = this.configObj.pref('save_notice_done');
+
+        if (done !== 'all') {
+          const note = svgEditor.i18next.t('notification.saveFromBrowser', { type: 'SVG' });
+
+          this.configObj.pref('save_notice_done', 'all');
+          if (done !== 'part') {
+            seAlert(note);
+          }
+        }
+      },
+      async open () {
+        const ok = await this.openPrep();
+        if (ok === 'Cancel') { return; }
+        this.svgCanvas.clear();
+        const input = document.createElement('input');
+        input.type = 'file';
+        input.addEventListener('change', (e) => {
+          // getting a hold of the file reference
+          const file = e.target.files[0];
+          // setting up the reader
+          const reader = new FileReader();
+          reader.readAsText(file, 'UTF-8');
+          // here we tell the reader what to do when it's done reading...
+          reader.addEventListener('load', async (readerEvent) => {
+            const content = readerEvent.target.result;
+            await this.loadSvgString(content);
+            this.updateCanvas();
+          });
+        });
+        input.click();
+      }
+    });
+  }
+};
diff --git a/cypress/integration/ui/__snapshots__/scenario1.js.snap b/cypress/integration/ui/__snapshots__/scenario1.js.snap
index 6c29fcecd..ee4d81cdc 100644
--- a/cypress/integration/ui/__snapshots__/scenario1.js.snap
+++ b/cypress/integration/ui/__snapshots__/scenario1.js.snap
@@ -101,7 +101,109 @@ exports[`use all parts of svg-edit > check tool_shape #0`] = `
 </svg>
 `;
 
-exports[`use all parts of svg-edit > check mode_connect #0`] = `
+exports[`use all parts of svg-edit > check tool_rect_square #0`] = `
+<svg
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:svg="http://www.w3.org/2000/svg"
+  width="640"
+  height="480"
+  id="svgcontent"
+  overflow="visible"
+  x="640"
+  y="480"
+  viewBox="0 0 640 480"
+>
+  <g class="layer" style="pointer-events:all">
+    <title style="pointer-events:inherit">Layer 1</title>
+    <line
+      fill="none"
+      stroke="#000000"
+      stroke-width="5"
+      style="pointer-events:inherit"
+      x1="200"
+      y1="200"
+      x2="220"
+      y2="220"
+      id="svg_1"
+      fill-opacity="1"
+      stroke-opacity="1"
+      transform="rotate(43.2643 210 210)"
+    ></line>
+    <path
+      fill="#FF0000"
+      stroke="#000000"
+      stroke-width="5"
+      style="pointer-events:inherit"
+      d="m309.60653833763354,262.7975086895094 c37.05917105721012,-106.31729401658639 182.2582183141481,0 0,136.6936637356111 c-182.2582183141481,-136.6936637356111 -37.05917105721012,-243.0109577521975 0,-136.6936637356111 z"
+      id="svg_2"
+      fill-opacity="1"
+      stroke-opacity="1"
+    ></path>
+    <rect
+      fill="#FF0000"
+      stroke="#000000"
+      stroke-width="5"
+      opacity="0.5"
+      style="pointer-events:inherit"
+      x="296"
+      y="136"
+      width="150"
+      height="120"
+      id="svg_3"
+    >
+      <animate
+        attributeName="opacity"
+        begin="indefinite"
+        dur="0.2"
+        fill="freeze"
+        to="1"
+      ></animate>
+    </rect>
+    <rect
+      fill="#FF0000"
+      stroke="#000000"
+      stroke-width="5"
+      opacity="0.5"
+      style="pointer-events:inherit"
+      x="446"
+      y="76"
+      width="180"
+      height="180"
+      id="svg_4"
+    >
+      <animate
+        attributeName="opacity"
+        begin="indefinite"
+        dur="0.2"
+        fill="freeze"
+        to="1"
+      ></animate>
+    </rect>
+    <rect
+      fill="#FF0000"
+      stroke="#000000"
+      stroke-width="5"
+      opacity="0.5"
+      style="pointer-events:inherit"
+      x="216"
+      y="156"
+      width="100"
+      height="100"
+      id="svg_5"
+    >
+      <animate
+        attributeName="opacity"
+        begin="indefinite"
+        dur="0.2"
+        fill="freeze"
+        to="1"
+      ></animate>
+    </rect>
+  </g>
+</svg>
+`;
+
+exports[`use all parts of svg-edit > check tool_image #0`] = `
 <svg
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg"
@@ -156,13 +258,37 @@ exports[`use all parts of svg-edit > check mode_connect #0`] = `
       fill="#FF0000"
       stroke="#000000"
       stroke-width="5"
-      opacity="0.5"
       style="pointer-events:inherit"
       x="446"
       y="76"
       width="180"
       height="180"
       id="svg_4"
+      fill-opacity="1"
+      stroke-opacity="1"
+    ></rect>
+    <rect
+      fill="#FF0000"
+      stroke="#000000"
+      stroke-width="5"
+      style="pointer-events:inherit"
+      x="216"
+      y="156"
+      width="100"
+      height="100"
+      id="svg_5"
+      fill-opacity="1"
+      stroke-opacity="1"
+    ></rect>
+    <image
+      x="296"
+      y="176"
+      width="20"
+      height="20"
+      id="svg_6"
+      opacity="0.5"
+      style="pointer-events:inherit"
+      xlink:href="./images/logo.svg"
     >
       <animate
         attributeName="opacity"
@@ -171,7 +297,7 @@ exports[`use all parts of svg-edit > check mode_connect #0`] = `
         fill="freeze"
         to="1"
       ></animate>
-    </rect>
+    </image>
   </g>
 </svg>
 `;
diff --git a/cypress/integration/ui/scenario1.js b/cypress/integration/ui/scenario1.js
index becc54092..9813aed0b 100644
--- a/cypress/integration/ui/scenario1.js
+++ b/cypress/integration/ui/scenario1.js
@@ -49,7 +49,7 @@ describe('use all parts of svg-edit', function () {
       .trigger('mouseup', { force: true });
     cy.get('#svgcontent').toMatchSnapshot();
   });
-  it('check mode_connect', function () {
+  it('check tool_rect_square', function () {
     cy.get('#tool_rect').click({ force: true });
     cy.get('#svgcontent')
       .trigger('mousedown', 100, -60, { force: true })
@@ -60,24 +60,32 @@ describe('use all parts of svg-edit', function () {
       .trigger('mousedown', 250, -60, { force: true })
       .trigger('mousemove', 430, 120, { force: true })
       .trigger('mouseup', { force: true });
-    cy.get('#tool_select').click({ force: true });
-    cy.get('#mode_connect').click({ force: true });
-
+    cy.get('#tool_fhrect')
+      .click({ force: true });
     cy.get('#svgcontent')
-      .trigger('mousedown', -10, -10, { force: true })
-      .trigger('mousemove', -180, -180, { force: true })
-      .trigger('mouseup', { force: true });
+      .trigger('mousedown', 20, 80, { force: true })
+      .trigger('mousemove', 120, 80, { force: true })
+      .trigger('mousemove', 120, 180, { force: true })
+      .trigger('mousemove', 20, 180, { force: true })
+      .trigger('mousemove', 20, 80, { force: true })
+      .trigger('mouseup', 20, 80, { force: true });
     cy.get('#svgcontent').toMatchSnapshot();
   });
-  /* it('check tool_image', function () {
+  it('check tool_image', function () {
 
     cy.get('#tool_image').click({ force: true });
     cy.get('#svgcontent')
-      .trigger('mousemove', 100, 100, { force: true })
       .trigger('mousedown', 100, 100, { force: true })
+      .trigger('mousemove', 120, 120, { force: true })
       .trigger('mouseup', { force: true });
-    cy.on('window:confirm', () => true);
+    // eslint-disable-next-line promise/catch-or-return
+    cy.window()
+      // eslint-disable-next-line promise/always-return
+      .then(($win) => {
+        cy.stub($win, 'prompt').returns('./images/logo.svg');
+        cy.contains('OK');
+      });
     cy.get('#svgcontent').toMatchSnapshot();
-  }); */
+  });
 
 });
diff --git a/package-lock.json b/package-lock.json
index 2180c530d..6fc909afb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
       "license": "(MIT AND Apache-2.0 AND ISC AND LGPL-3.0-or-later AND X11)",
       "dependencies": {
         "@babel/polyfill": "7.12.1",
+        "browser-fs-access": "^0.20.4",
         "canvg": "3.0.7",
         "core-js": "3.16.2",
         "elix": "15.0.0",
@@ -5299,6 +5300,11 @@
         "node": ">= 10.16.0"
       }
     },
+    "node_modules/browser-fs-access": {
+      "version": "0.20.4",
+      "resolved": "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.20.4.tgz",
+      "integrity": "sha512-rSbY1AIoDe+fvYZ1LiRDdKBnytfsd1nN/GKS/DRZAhaJkz3cfbp14IHw5lk4FFWBelD6Sw6EtdnAI990ZuBZjg=="
+    },
     "node_modules/browser-pack": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz",
@@ -26396,6 +26402,11 @@
         "duplexer": "0.1.1"
       }
     },
+    "browser-fs-access": {
+      "version": "0.20.4",
+      "resolved": "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.20.4.tgz",
+      "integrity": "sha512-rSbY1AIoDe+fvYZ1LiRDdKBnytfsd1nN/GKS/DRZAhaJkz3cfbp14IHw5lk4FFWBelD6Sw6EtdnAI990ZuBZjg=="
+    },
     "browser-pack": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz",
diff --git a/package.json b/package.json
index 89e91820d..66ab46c1b 100644
--- a/package.json
+++ b/package.json
@@ -77,6 +77,7 @@
   ],
   "dependencies": {
     "@babel/polyfill": "7.12.1",
+    "browser-fs-access": "^0.20.4",
     "canvg": "3.0.7",
     "core-js": "3.16.2",
     "elix": "15.0.0",
diff --git a/src/editor/Editor.js b/src/editor/Editor.js
index 2b3887e45..c5e58ff8e 100644
--- a/src/editor/Editor.js
+++ b/src/editor/Editor.js
@@ -180,38 +180,6 @@ class Editor extends EditorStartup {
   * @returns {void}
   */
 
-  /**
-  * Allows one to override default SVGEdit `open`, `save`, and
-  * `export` editor behaviors.
-  * @function module:SVGthis.setCustomHandlers
-  * @param {module:SVGthis.CustomHandler} opts Extension mechanisms may call `setCustomHandlers` with three functions: `opts.open`, `opts.save`, and `opts.exportImage`
-  * @returns {Promise<void>}
-  */
-
-  /**
-   * @param {PlainObject} opts
-   * @returns {Promise<PlainObject>}
-   */
-  setCustomHandlers(opts) {
-    return this.ready(() => {
-      if (opts.open) {
-        this.svgCanvas.open = opts.open.bind(this);
-      }
-      if (opts.save) {
-        this.showSaveWarning = false;
-        this.svgCanvas.bind('saved', opts.save.bind(this));
-      }
-      if (opts.exportImage) {
-        this.customExportImage = opts.exportImage.bind(this);
-        this.svgCanvas.bind('exported', this.customExportImage); // canvg and our RGBColor will be available to the method
-      }
-      if (opts.exportPDF) {
-        this.customExportPDF = opts.exportPDF.bind(this);
-        this.svgCanvas.bind('exportedPDF', this.customExportPDF); // jsPDF and our RGBColor will be available to the method
-      }
-    });
-  }
-
   /**
    * @function module:SVGthis.randomizeIds
    * @param {boolean} arg
diff --git a/src/editor/MainMenu.js b/src/editor/MainMenu.js
index 27dd08acc..bac713063 100644
--- a/src/editor/MainMenu.js
+++ b/src/editor/MainMenu.js
@@ -129,20 +129,6 @@ class MainMenu {
     this.editor.updateCanvas();
     this.hidePreferences();
   }
-
-  /**
-   *
-   * @returns {void}
-   */
-  clickSave() {
-    // In the future, more options can be provided here
-    const saveOpts = {
-      images: this.editor.configObj.pref("img_save"),
-      round_digits: 6
-    };
-    this.editor.svgCanvas.save(saveOpts);
-  }
-
   /**
    *
    * @param e
@@ -210,16 +196,6 @@ class MainMenu {
     }
   }
 
-  /**
-   * By default,  this.editor.svgCanvas.open() is a no-op. It is up to an extension
-   *  mechanism (opera widget, etc.) to call `setCustomHandlers()` which
-   *  will make it do something.
-   * @returns {void}
-   */
-  clickOpen() {
-    this.editor.svgCanvas.open();
-  }
-
   /**
    *
    * @returns {void}
@@ -307,9 +283,6 @@ class MainMenu {
     // eslint-disable-next-line no-unsanitized/property
     template.innerHTML = `
     <se-menu id="main_button" label="SVG-Edit" src="${imgPath}/logo.svg" alt="logo">
-        <se-menu-item id="tool_clear" label="${i18next.t('tools.new_doc')}" shortcut="N" src="${imgPath}/new.svg"></se-menu-item>
-        <se-menu-item id="tool_open" label="${i18next.t('tools.open_doc')}" src="${imgPath}/open.svg"></se-menu-item>
-        <se-menu-item id="tool_save" label="${i18next.t('tools.save_doc')}" shortcut="S" src="${imgPath}/saveImg.svg"></se-menu-item>
         <se-menu-item id="tool_import" label="${i18next.t('tools.import_doc')}" src="${imgPath}/importImg.svg"></se-menu-item>
         <se-menu-item id="tool_export" label="${i18next.t('tools.export_img')}" src="${imgPath}/export.svg"></se-menu-item>
         <se-menu-item id="tool_docprops" label="${i18next.t('tools.docprops')}" shortcut="D" src="${imgPath}/docprop.svg"></se-menu-item>
@@ -324,29 +297,10 @@ class MainMenu {
      * Associate all button actions as well as non-button keyboard shortcuts.
      */
 
-    $id("tool_clear").addEventListener("click", this.clickClear.bind(this));
-    $id("tool_open").addEventListener("click", (e) => {
-      e.preventDefault();
-      this.clickOpen();
-      window.dispatchEvent(new CustomEvent("openImage"));
-    });
     $id("tool_import").addEventListener("click", () => {
       this.clickImport();
       window.dispatchEvent(new CustomEvent("importImages"));
     });
-    $id("tool_save").addEventListener(
-      "click",
-      function() {
-        const $editorDialog = $id("se-svg-editor-dialog");
-        const editingsource = $editorDialog.getAttribute("dialog") === "open";
-        if (editingsource) {
-          this.saveSourceEditor();
-        } else {
-          this.clickSave();
-        }
-      }.bind(this)
-    );
-    // this.clickExport.bind(this)
     $id("tool_export").addEventListener("click", function() {
       document
         .getElementById("se-export-dialog")
diff --git a/src/editor/extensions/ext-opensave/ext-opensave.js b/src/editor/extensions/ext-opensave/ext-opensave.js
index b856d2fec..fd9a463f3 100644
--- a/src/editor/extensions/ext-opensave/ext-opensave.js
+++ b/src/editor/extensions/ext-opensave/ext-opensave.js
@@ -1,4 +1,4 @@
-/* globals seAlert */
+/* globals seConfirm */
 /**
  * @file ext-opensave.js
  *
@@ -15,64 +15,167 @@
    * @listens module:svgcanvas.SvgCanvas#event:saved
    * @returns {void}
    */
+import { fileOpen, fileSave } from 'browser-fs-access';
 
-export default {
-  name: 'opensave',
-  init ({ encode64 }) {
-    const svgEditor = this;
+const name = "opensave";
+let handle = null;
 
-    svgEditor.setCustomHandlers({
-      save (win, svg) {
-        this.showSaveWarning = false;
+const loadExtensionTranslation = async function (svgEditor) {
+  let translationModule;
+  const lang = svgEditor.configObj.pref('lang');
+  try {
+    // eslint-disable-next-line no-unsanitized/method
+    translationModule = await import(`./locale/${lang}.js`);
+  } catch (_error) {
+    // eslint-disable-next-line no-console
+    console.warn(`Missing translation (${lang}) for ${name} - using 'en'`);
+    // eslint-disable-next-line no-unsanitized/method
+    translationModule = await import(`./locale/en.js`);
+  }
+  svgEditor.i18next.addResourceBundle(lang, 'translation', translationModule.default, true, true);
+};
 
-        // by default, we add the XML prolog back, systems integrating SVG-edit (wikis, CMSs)
-        // can just provide their own custom save handler and might not want the XML prolog
-        svg = '<?xml version="1.0"?>\n' + svg;
+export default {
+  name,
+  async init(_S) {
+    const svgEditor = this;
+    const { imgPath } = svgEditor.configObj.curConfig;
+    const { svgCanvas } = svgEditor;
+    const { $id } = svgCanvas;
+    await loadExtensionTranslation(svgEditor);
 
-        // Since saving SVGs by opening a new window was removed in Chrome use artificial link-click
-        // https://stackoverflow.com/questions/45603201/window-is-not-allowed-to-navigate-top-frame-navigations-to-data-urls
-        const a = document.createElement('a');
-        a.href = 'data:image/svg+xml;base64,' + encode64(svg);
-        a.download = 'icon.svg';
-        a.style.display = 'none';
-        document.body.append(a); // Need to append for Firefox
+    /**
+     * @fires module:svgcanvas.SvgCanvas#event:ext_onNewDocument
+     * @returns {void}
+     */
+    const clickClear = async function () {
+      const [ x, y ] = svgEditor.configObj.curConfig.dimensions;
+      const ok = await seConfirm(svgEditor.i18next.t('notification.QwantToClear'));
+      if (ok === "Cancel") {
+        return;
+      }
+      svgEditor.leftPanel.clickSelect();
+      svgEditor.svgCanvas.clear();
+      svgEditor.svgCanvas.setResolution(x, y);
+      svgEditor.updateCanvas(true);
+      svgEditor.zoomImage();
+      svgEditor.layersPanel.populateLayers();
+      svgEditor.topPanel.updateContextPanel();
+      svgEditor.svgCanvas.runExtensions("onNewDocument");
+    };
 
-        a.click();
+    /**
+     * By default,  this.editor.svgCanvas.open() is a no-op. It is up to an extension
+     *  mechanism (opera widget, etc.) to call `setCustomHandlers()` which
+     *  will make it do something.
+     * @returns {void}
+     */
+    const clickOpen = async function () {
+      // ask user before clearing an unsaved SVG
+      const response = await svgEditor.openPrep();
+      if (response === 'Cancel') { return; }
+      svgCanvas.clear();
+      try {
+        const blob = await fileOpen({
+          mimeTypes: [ 'image/*' ]
+        });
+        const svgContent = await blob.text();
+        await svgEditor.loadSvgString(svgContent);
+        svgEditor.updateCanvas();
+      } catch (err) {
+        if (err.name !== 'AbortError') {
+          return console.error(err);
+        }
+      }
+    };
 
-        // Alert will only appear the first time saved OR the
-        //   first time the bug is encountered
-        const done = this.configObj.pref('save_notice_done');
+    const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
+      const byteCharacters = atob(b64Data);
+      const byteArrays = [];
+      for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
+        const slice = byteCharacters.slice(offset, offset + sliceSize);
+        const byteNumbers = new Array(slice.length);
+        for (let i = 0; i < slice.length; i++) {
+          byteNumbers[i] = slice.charCodeAt(i);
+        }
+        const byteArray = new Uint8Array(byteNumbers);
+        byteArrays.push(byteArray);
+      }
+      const blob = new Blob(byteArrays, { type: contentType });
+      return blob;
+    };
 
-        if (done !== 'all') {
-          const note = svgEditor.i18next.t('notification.saveFromBrowser', { type: 'SVG' });
+    /**
+     *
+     * @returns {void}
+     */
+    const clickSave = async function (type, _) {
+      const $editorDialog = $id("se-svg-editor-dialog");
+      const editingsource = $editorDialog.getAttribute("dialog") === "open";
+      if (editingsource) {
+        svgEditor.saveSourceEditor();
+      } else {
+        // In the future, more options can be provided here
+        const saveOpts = {
+          images: svgEditor.configObj.pref("img_save"),
+          round_digits: 6
+        };
+        // remove the selected outline before serializing
+        svgCanvas.clearSelection();
+        // Update save options if provided
+        if (saveOpts) {
+          const saveOptions = svgCanvas.mergeDeep(svgCanvas.getSvgOption(), saveOpts);
+          for (const [ key, value ] of Object.entries(saveOptions)) {
+            svgCanvas.setSvgOption(key, value);
+          }
+        }
+        svgCanvas.setSvgOption('apply', true);
 
-          this.configObj.pref('save_notice_done', 'all');
-          if (done !== 'part') {
-            seAlert(note);
+        // no need for doctype, see https://jwatt.org/svg/authoring/#doctype-declaration
+        const svg = '<?xml version="1.0"?>\n' + svgCanvas.svgCanvasToString();
+        const b64Data = svgCanvas.encode64(svg);
+        const blob = b64toBlob(b64Data, 'image/svg+xml');
+        try {
+          if(type === "save" && handle !== null) {
+            const throwIfExistingHandleNotGood = false;
+            handle = await fileSave(blob, {
+              fileName: 'icon.svg',
+              extensions: [ '.svg' ]
+            }, handle, throwIfExistingHandleNotGood);
+          } else {
+            handle = await fileSave(blob, {
+              fileName: 'icon.svg',
+              extensions: [ '.svg' ]
+            });
+          }
+        } catch (err) {
+          if (err.name !== 'AbortError') {
+            return console.error(err);
           }
         }
-      },
-      async open () {
-        const ok = await this.openPrep();
-        if (ok === 'Cancel') { return; }
-        this.svgCanvas.clear();
-        const input = document.createElement('input');
-        input.type = 'file';
-        input.addEventListener('change', (e) => {
-          // getting a hold of the file reference
-          const file = e.target.files[0];
-          // setting up the reader
-          const reader = new FileReader();
-          reader.readAsText(file, 'UTF-8');
-          // here we tell the reader what to do when it's done reading...
-          reader.addEventListener('load', async (readerEvent) => {
-            const content = readerEvent.target.result;
-            await this.loadSvgString(content);
-            this.updateCanvas();
-          });
-        });
-        input.click();
       }
-    });
+    };
+
+    return {
+      name: svgEditor.i18next.t(`${name}:name`),
+      // The callback should be used to load the DOM with the appropriate UI items
+      callback() {
+        // eslint-disable-next-line no-unsanitized/property
+        const buttonTemplate = `
+        <se-menu-item id="tool_clear" label="${svgEditor.i18next.t('opensave.new_doc')}" shortcut="N" src="${imgPath}/new.svg"></se-menu-item>`;
+        svgCanvas.insertChildAtIndex($id('main_button'), buttonTemplate, 0);
+        const openButtonTemplate = `<se-menu-item id="tool_open" label="${svgEditor.i18next.t('opensave.open_image_doc')}" src="${imgPath}/open.svg"></se-menu-item>`;
+        svgCanvas.insertChildAtIndex($id('main_button'), openButtonTemplate, 1);
+        const saveButtonTemplate = `<se-menu-item id="tool_save" label="${svgEditor.i18next.t('opensave.save_doc')}" shortcut="S" src="${imgPath}/saveImg.svg"></se-menu-item>`;
+        svgCanvas.insertChildAtIndex($id('main_button'), saveButtonTemplate, 2);
+        const saveAsButtonTemplate = `<se-menu-item id="tool_save_as" label="${svgEditor.i18next.t('opensave.save_as_doc')}" src="${imgPath}/saveImg.svg"></se-menu-item>`;
+        svgCanvas.insertChildAtIndex($id('main_button'), saveAsButtonTemplate, 3);
+        // handler
+        $id("tool_clear").addEventListener("click", clickClear.bind(this));
+        $id("tool_open").addEventListener("click", clickOpen.bind(this));
+        $id("tool_save").addEventListener("click", clickSave.bind(this, "save"));
+        $id("tool_save_as").addEventListener("click", clickSave.bind(this, "saveas"));
+      }
+    };
   }
 };
diff --git a/src/editor/extensions/ext-opensave/locale/en.js b/src/editor/extensions/ext-opensave/locale/en.js
new file mode 100644
index 000000000..bedd8cf00
--- /dev/null
+++ b/src/editor/extensions/ext-opensave/locale/en.js
@@ -0,0 +1,8 @@
+export default {
+  opensave: {
+    new_doc: 'New Image',
+    open_image_doc: 'Open SVG',
+    save_doc: 'Save SVG',
+    save_as_doc: 'Save as SVG'
+  }
+};
diff --git a/src/editor/extensions/ext-opensave/locale/fr.js b/src/editor/extensions/ext-opensave/locale/fr.js
new file mode 100644
index 000000000..7704f6d65
--- /dev/null
+++ b/src/editor/extensions/ext-opensave/locale/fr.js
@@ -0,0 +1,8 @@
+export default {
+  opensave: {
+    new_doc: 'Nouvelle image',
+    open_image_doc: 'Ouvrir le SVG',
+    save_doc: 'Enregistrer l\'image',
+    save_as_doc: 'Enregistrer en tant qu\'image'
+  }
+};
diff --git a/src/editor/extensions/ext-opensave/locale/zh-CN.js b/src/editor/extensions/ext-opensave/locale/zh-CN.js
new file mode 100755
index 000000000..c1f47c07c
--- /dev/null
+++ b/src/editor/extensions/ext-opensave/locale/zh-CN.js
@@ -0,0 +1,8 @@
+export default {
+  opensave: {
+    new_doc: '新图片',
+    open_image_doc: '打开 SVG',
+    save_doc: '保存图像',
+    save_as_doc: '另存为图像'
+  }
+};
diff --git a/src/editor/locale/lang.af.js b/src/editor/locale/lang.af.js
index 1027a18f0..5c3b74326 100644
--- a/src/editor/locale/lang.af.js
+++ b/src/editor/locale/lang.af.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'grootste voorwerp',
     selected_objects: 'verkose voorwerpe',
     smallest_object: 'kleinste voorwerp',
-    new_doc: 'Nuwe Beeld',
-    open_doc: 'Open Beeld',
     export_img: 'Export',
-    save_doc: 'Slaan Beeld',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Rig Middel',
diff --git a/src/editor/locale/lang.ar.js b/src/editor/locale/lang.ar.js
index 3e2673cc4..ee393452b 100644
--- a/src/editor/locale/lang.ar.js
+++ b/src/editor/locale/lang.ar.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'أكبر كائن',
     selected_objects: 'انتخب الأجسام',
     smallest_object: 'أصغر كائن',
-    new_doc: 'صورة جديدة',
-    open_doc: 'فتح الصورة',
     export_img: 'Export',
-    save_doc: 'حفظ صورة',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'محاذاة القاع',
     align_center: 'مركز محاذاة',
diff --git a/src/editor/locale/lang.az.js b/src/editor/locale/lang.az.js
index bc7bae512..2d50b6597 100644
--- a/src/editor/locale/lang.az.js
+++ b/src/editor/locale/lang.az.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'largest object',
     selected_objects: 'selected objects',
     smallest_object: 'smallest object',
-    new_doc: 'New Image',
-    open_doc: 'Open SVG',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Align Center',
diff --git a/src/editor/locale/lang.be.js b/src/editor/locale/lang.be.js
index 071cf304a..64182cb76 100644
--- a/src/editor/locale/lang.be.js
+++ b/src/editor/locale/lang.be.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'найбуйнейшы аб&#39;ект',
     selected_objects: 'выбранымі аб&#39;ектамі',
     smallest_object: 'маленькі аб&#39;ект',
-    new_doc: 'Новае выява',
-    open_doc: 'Адкрыць выява',
     export_img: 'Export',
-    save_doc: 'Захаваць малюнак',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Лінаваць па ніжнім краю',
     align_center: 'Лінаваць па цэнтру',
diff --git a/src/editor/locale/lang.bg.js b/src/editor/locale/lang.bg.js
index 783a2ee6c..cae8dd2ec 100644
--- a/src/editor/locale/lang.bg.js
+++ b/src/editor/locale/lang.bg.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'най-големият обект',
     selected_objects: 'избраните обекти',
     smallest_object: 'най-малката обект',
-    new_doc: 'Ню Имидж',
-    open_doc: 'Отворете изображението',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Привеждане Отдолу',
     align_center: 'Подравняване в средата',
diff --git a/src/editor/locale/lang.ca.js b/src/editor/locale/lang.ca.js
index ca3157c72..6f56c947e 100644
--- a/src/editor/locale/lang.ca.js
+++ b/src/editor/locale/lang.ca.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Obrir imatge',
     export_img: 'Export',
     save_doc: 'Guardar imatge',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Alinear baix',
     align_center: 'Alinear al centre',
diff --git a/src/editor/locale/lang.cy.js b/src/editor/locale/lang.cy.js
index 031a97ed5..72c466e6f 100644
--- a/src/editor/locale/lang.cy.js
+++ b/src/editor/locale/lang.cy.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Delwedd Agored',
     export_img: 'Export',
     save_doc: 'Cadw Delwedd',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Alinio Gwaelod',
     align_center: 'Alinio Center',
diff --git a/src/editor/locale/lang.da.js b/src/editor/locale/lang.da.js
index 3bd6c1989..85ce81794 100644
--- a/src/editor/locale/lang.da.js
+++ b/src/editor/locale/lang.da.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'største objekt',
     selected_objects: 'valgte objekter',
     smallest_object: 'mindste objekt',
-    new_doc: 'Nyt billede',
-    open_doc: 'Open SVG',
     export_img: 'Export',
-    save_doc: 'Gem billede',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Juster Bottom',
     align_center: 'Centrer',
diff --git a/src/editor/locale/lang.el.js b/src/editor/locale/lang.el.js
index fa9396186..fa64435c2 100644
--- a/src/editor/locale/lang.el.js
+++ b/src/editor/locale/lang.el.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Άνοιγμα εικόνας',
     export_img: 'Export',
     save_doc: 'Αποθήκευση εικόνας',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Στοίχισηκάτω',
     align_center: 'Στοίχισηστοκέντρο',
diff --git a/src/editor/locale/lang.en.js b/src/editor/locale/lang.en.js
index 7d9ba8f32..932304826 100644
--- a/src/editor/locale/lang.en.js
+++ b/src/editor/locale/lang.en.js
@@ -108,11 +108,8 @@ export default {
     largest_object: 'largest object',
     selected_objects: 'selected objects',
     smallest_object: 'smallest object',
-    new_doc: 'New Image',
-    open_doc: 'Open SVG',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Align Center',
diff --git a/src/editor/locale/lang.et.js b/src/editor/locale/lang.et.js
index 5d10396b2..e4620d749 100644
--- a/src/editor/locale/lang.et.js
+++ b/src/editor/locale/lang.et.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Pildi avamine',
     export_img: 'Export',
     save_doc: 'Salvesta pilt',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Viia Bottom',
     align_center: 'Keskele joondamine',
diff --git a/src/editor/locale/lang.fa.js b/src/editor/locale/lang.fa.js
index bb3cadb88..ce340d49c 100644
--- a/src/editor/locale/lang.fa.js
+++ b/src/editor/locale/lang.fa.js
@@ -111,7 +111,7 @@ export default {
     open_doc: '‫باز کردن تصویر ‬',
     export_img: 'Export',
     save_doc: '‫ذخیره تصویر ‬',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: '‫تراز پایین‬',
     align_center: '‫وسط چین‬',
diff --git a/src/editor/locale/lang.fi.js b/src/editor/locale/lang.fi.js
index 168958f69..b59302ce4 100644
--- a/src/editor/locale/lang.fi.js
+++ b/src/editor/locale/lang.fi.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'Suurin kohde',
     selected_objects: 'valittujen objektien',
     smallest_object: 'pienin kohde',
-    new_doc: 'Uusi kuva',
-    open_doc: 'Avaa kuva',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Keskitä',
diff --git a/src/editor/locale/lang.fy.js b/src/editor/locale/lang.fy.js
index ae0a903fc..fe0a367b5 100644
--- a/src/editor/locale/lang.fy.js
+++ b/src/editor/locale/lang.fy.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Ôfbielding iepenje',
     export_img: 'Export',
     save_doc: 'Ôfbielding bewarje',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Ûnder útlijne',
     align_center: 'Midden útlijne',
diff --git a/src/editor/locale/lang.ga.js b/src/editor/locale/lang.ga.js
index f15eb4fe4..b0ca32b38 100644
--- a/src/editor/locale/lang.ga.js
+++ b/src/editor/locale/lang.ga.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Íomhá Oscailte',
     export_img: 'Export',
     save_doc: 'Sábháil Íomhá',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Cineál Bun',
     align_center: 'Ailínigh sa Lár',
diff --git a/src/editor/locale/lang.gl.js b/src/editor/locale/lang.gl.js
index ee9134be5..aa7c2e7dd 100644
--- a/src/editor/locale/lang.gl.js
+++ b/src/editor/locale/lang.gl.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Abrir Imaxe',
     export_img: 'Export',
     save_doc: 'Gardar Imaxe',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align bottom',
     align_center: 'Centrar',
diff --git a/src/editor/locale/lang.he.js b/src/editor/locale/lang.he.js
index bcfe4f62f..d50591bf0 100755
--- a/src/editor/locale/lang.he.js
+++ b/src/editor/locale/lang.he.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'פתח תמונה',
     export_img: 'Export',
     save_doc: 'שמור תמונה',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'יישור תחתון',
     align_center: 'ישור לאמצע',
diff --git a/src/editor/locale/lang.hi.js b/src/editor/locale/lang.hi.js
index 056ecb180..8446c0f74 100644
--- a/src/editor/locale/lang.hi.js
+++ b/src/editor/locale/lang.hi.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'छवि खोलें',
     export_img: 'Export',
     save_doc: 'सहेजें छवि',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'तलमेंपंक्तिबद्धकरें',
     align_center: 'मध्य में समंजित करें',
diff --git a/src/editor/locale/lang.hr.js b/src/editor/locale/lang.hr.js
index f3c2850ca..ff8f322b2 100644
--- a/src/editor/locale/lang.hr.js
+++ b/src/editor/locale/lang.hr.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Otvori sliku',
     export_img: 'Export',
     save_doc: 'Spremanje slike',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Poravnaj dolje',
     align_center: 'Centriraj',
diff --git a/src/editor/locale/lang.hu.js b/src/editor/locale/lang.hu.js
index a540bcb32..48aed81e4 100644
--- a/src/editor/locale/lang.hu.js
+++ b/src/editor/locale/lang.hu.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Kép megnyitása',
     export_img: 'Export',
     save_doc: 'Kép mentése más',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Alulra igazítás',
     align_center: 'Középre igazítás',
diff --git a/src/editor/locale/lang.hy.js b/src/editor/locale/lang.hy.js
index bdebebb4c..85e2e7636 100644
--- a/src/editor/locale/lang.hy.js
+++ b/src/editor/locale/lang.hy.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'largest object',
     selected_objects: 'elected objects',
     smallest_object: 'smallest object',
-    new_doc: 'New Image',
-    open_doc: 'Open SVG',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Align Center',
diff --git a/src/editor/locale/lang.id.js b/src/editor/locale/lang.id.js
index 0d23baba6..606e3c6d1 100644
--- a/src/editor/locale/lang.id.js
+++ b/src/editor/locale/lang.id.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'objek terbesar',
     selected_objects: 'objek terpilih',
     smallest_object: 'objek terkecil',
-    new_doc: 'Gambar Baru',
-    open_doc: 'Membuka Image',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Rata Bottom',
     align_center: 'Rata Tengah',
diff --git a/src/editor/locale/lang.is.js b/src/editor/locale/lang.is.js
index ebe482a32..018a3200d 100644
--- a/src/editor/locale/lang.is.js
+++ b/src/editor/locale/lang.is.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'stærsti hlutinn',
     selected_objects: 'kjörinn hlutir',
     smallest_object: 'lítill hluti',
-    new_doc: 'New Image',
-    open_doc: 'Opna mynd',
     export_img: 'Export',
-    save_doc: 'Spara Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Jafna Bottom',
     align_center: 'Jafna Center',
diff --git a/src/editor/locale/lang.ja.js b/src/editor/locale/lang.ja.js
index 1d4ccf4bb..d381f0251 100644
--- a/src/editor/locale/lang.ja.js
+++ b/src/editor/locale/lang.ja.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'イメージを開く',
     export_img: 'Export',
     save_doc: '画像を保存',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: '下揃え',
     align_center: '中央揃え',
diff --git a/src/editor/locale/lang.ko.js b/src/editor/locale/lang.ko.js
index 5200387c2..27abf1112 100644
--- a/src/editor/locale/lang.ko.js
+++ b/src/editor/locale/lang.ko.js
@@ -111,7 +111,7 @@ export default {
     open_doc: '오픈 이미지',
     export_img: 'Export',
     save_doc: '이미지 저장',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: '히프 정렬',
     align_center: '정렬 센터',
diff --git a/src/editor/locale/lang.lt.js b/src/editor/locale/lang.lt.js
index 11e4d980e..c0aa8b124 100644
--- a/src/editor/locale/lang.lt.js
+++ b/src/editor/locale/lang.lt.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'didžiausias objektas',
     selected_objects: 'išrinktas objektai',
     smallest_object: 'mažiausias objektą',
-    new_doc: 'New Image',
-    open_doc: 'Atidaryti atvaizdą',
     export_img: 'Export',
-    save_doc: 'Išsaugoti nuotrauką',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Lygiuoti apačioje',
     align_center: 'Lygiuoti',
diff --git a/src/editor/locale/lang.lv.js b/src/editor/locale/lang.lv.js
index 79c6b8453..671f2299b 100644
--- a/src/editor/locale/lang.lv.js
+++ b/src/editor/locale/lang.lv.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'lielākais objekts',
     selected_objects: 'ievēlēts objekti',
     smallest_object: 'mazākais objekts',
-    new_doc: 'New Image',
-    open_doc: 'Open SVG',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Līdzināt Bottom',
     align_center: 'Līdzināt uz centru',
diff --git a/src/editor/locale/lang.mk.js b/src/editor/locale/lang.mk.js
index 7de7cee2a..4e28ade69 100644
--- a/src/editor/locale/lang.mk.js
+++ b/src/editor/locale/lang.mk.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Отвори слика',
     export_img: 'Export',
     save_doc: 'Зачувај слика',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Центрирано',
diff --git a/src/editor/locale/lang.ms.js b/src/editor/locale/lang.ms.js
index d4d6c64d3..9fc3c53e5 100644
--- a/src/editor/locale/lang.ms.js
+++ b/src/editor/locale/lang.ms.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'objek terbesar',
     selected_objects: 'objek terpilih',
     smallest_object: 'objek terkecil',
-    new_doc: 'Imej Baru',
-    open_doc: 'Membuka Image',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Rata Bottom',
     align_center: 'Rata Tengah',
diff --git a/src/editor/locale/lang.mt.js b/src/editor/locale/lang.mt.js
index 4f50c7967..7ef159afc 100644
--- a/src/editor/locale/lang.mt.js
+++ b/src/editor/locale/lang.mt.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'akbar oġġett',
     selected_objects: 'oġġetti elett',
     smallest_object: 'iżgħar oġġett',
-    new_doc: 'Image New',
-    open_doc: 'Open SVG',
     export_img: 'Export',
-    save_doc: 'Image Save',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Tallinja Bottom',
     align_center: 'Tallinja Center',
diff --git a/src/editor/locale/lang.no.js b/src/editor/locale/lang.no.js
index 4315dafb1..e6e3057f5 100644
--- a/src/editor/locale/lang.no.js
+++ b/src/editor/locale/lang.no.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'største objekt',
     selected_objects: 'velges objekter',
     smallest_object: 'minste objekt',
-    new_doc: 'New Image',
-    open_doc: 'Åpne Image',
     export_img: 'Export',
-    save_doc: 'Lagre bilde',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Midtstill',
diff --git a/src/editor/locale/lang.pt-PT.js b/src/editor/locale/lang.pt-PT.js
index 1f8e3a3a7..049ed220b 100644
--- a/src/editor/locale/lang.pt-PT.js
+++ b/src/editor/locale/lang.pt-PT.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Abrir Imagem',
     export_img: 'Export',
     save_doc: 'Salvar Imagem',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Alinhar ao centro',
diff --git a/src/editor/locale/lang.sk.js b/src/editor/locale/lang.sk.js
index 903a2f8e6..c9ba63d52 100644
--- a/src/editor/locale/lang.sk.js
+++ b/src/editor/locale/lang.sk.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Otvoriť obrázok',
     export_img: 'Export',
     save_doc: 'Uložiť obrázok',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Zarovnať element na stránku',
     align_bottom: 'Zarovnať dole',
     align_center: 'Zarovnať na stred',
diff --git a/src/editor/locale/lang.sq.js b/src/editor/locale/lang.sq.js
index 663b458b1..f559de7ae 100644
--- a/src/editor/locale/lang.sq.js
+++ b/src/editor/locale/lang.sq.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'madh objekt',
     selected_objects: 'objektet e zgjedhur',
     smallest_object: 'objektit më të vogël',
-    new_doc: 'New Image',
-    open_doc: 'Image Hapur',
     export_img: 'Export',
-    save_doc: 'Image Ruaj',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Align Center',
diff --git a/src/editor/locale/lang.sr.js b/src/editor/locale/lang.sr.js
index 1685384bc..63b9fe7e3 100644
--- a/src/editor/locale/lang.sr.js
+++ b/src/editor/locale/lang.sr.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Отвори слике',
     export_img: 'Export',
     save_doc: 'Сачувај слика',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Поравнај доле',
     align_center: 'Поравнај по центру',
diff --git a/src/editor/locale/lang.sv.js b/src/editor/locale/lang.sv.js
index a370367ef..4c8ab8608 100644
--- a/src/editor/locale/lang.sv.js
+++ b/src/editor/locale/lang.sv.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'största objekt',
     selected_objects: 'valda objekt',
     smallest_object: 'minsta objektet',
-    new_doc: 'New Image',
-    open_doc: 'Öppna bild',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Centrera',
diff --git a/src/editor/locale/lang.sw.js b/src/editor/locale/lang.sw.js
index 12dc3c5dd..8987ea86c 100644
--- a/src/editor/locale/lang.sw.js
+++ b/src/editor/locale/lang.sw.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'ukubwa object',
     selected_objects: 'waliochaguliwa vitu',
     smallest_object: 'minsta object',
-    new_doc: 'New Image',
-    open_doc: 'Open SVG',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Align Center',
diff --git a/src/editor/locale/lang.th.js b/src/editor/locale/lang.th.js
index 48f5c859d..c127a5f9e 100644
--- a/src/editor/locale/lang.th.js
+++ b/src/editor/locale/lang.th.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'ภาพเปิด',
     export_img: 'Export',
     save_doc: 'บันทึกรูปภาพ',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'ด้านล่างชิด',
     align_center: 'จัดแนวกึ่งกลาง',
diff --git a/src/editor/locale/lang.tl.js b/src/editor/locale/lang.tl.js
index 7a026afc2..6140c032c 100644
--- a/src/editor/locale/lang.tl.js
+++ b/src/editor/locale/lang.tl.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Buksan ang Image',
     export_img: 'Export',
     save_doc: 'I-save ang Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Pantayin sa Ibaba',
     align_center: 'Pantayin sa Gitna',
diff --git a/src/editor/locale/lang.tr.js b/src/editor/locale/lang.tr.js
index a1643c3ec..ac1a41052 100644
--- a/src/editor/locale/lang.tr.js
+++ b/src/editor/locale/lang.tr.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Aç Resim',
     export_img: 'Export',
     save_doc: 'Görüntüyü Kaydet',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Ortala',
diff --git a/src/editor/locale/lang.uk.js b/src/editor/locale/lang.uk.js
index 378a4105a..de83ddb8b 100644
--- a/src/editor/locale/lang.uk.js
+++ b/src/editor/locale/lang.uk.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Відкрити зображення',
     export_img: 'Export',
     save_doc: 'Зберегти малюнок',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Вирівняти по нижньому краю',
     align_center: 'Вирівняти по центру',
diff --git a/src/editor/locale/lang.vi.js b/src/editor/locale/lang.vi.js
index 16aca1a64..cc23fa2a6 100644
--- a/src/editor/locale/lang.vi.js
+++ b/src/editor/locale/lang.vi.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'lớn nhất đối tượng',
     selected_objects: 'bầu các đối tượng',
     smallest_object: 'nhỏ đối tượng',
-    new_doc: 'Hình mới',
-    open_doc: 'Mở Image',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Căn giữa',
diff --git a/src/editor/locale/lang.yi.js b/src/editor/locale/lang.yi.js
index 5c207466f..1ae027b49 100644
--- a/src/editor/locale/lang.yi.js
+++ b/src/editor/locale/lang.yi.js
@@ -111,7 +111,7 @@ export default {
     open_doc: 'Open בילד',
     export_img: 'Export',
     save_doc: 'היט בילד',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'יינרייען באָטטאָם',
     align_center: 'יינרייען צענטער',
diff --git a/src/editor/locale/lang.zh-HK.js b/src/editor/locale/lang.zh-HK.js
index 93a7a189b..5d235d52d 100644
--- a/src/editor/locale/lang.zh-HK.js
+++ b/src/editor/locale/lang.zh-HK.js
@@ -111,7 +111,7 @@ export default {
     open_doc: '打开图像',
     export_img: 'Export',
     save_doc: '保存图像',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: '底部对齐',
     align_center: '居中对齐',
diff --git a/src/editor/locale/lang.zh-TW.js b/src/editor/locale/lang.zh-TW.js
index 5c67e7185..7b3311f31 100644
--- a/src/editor/locale/lang.zh-TW.js
+++ b/src/editor/locale/lang.zh-TW.js
@@ -111,7 +111,7 @@ export default {
     open_doc: '打開圖像',
     export_img: 'Export',
     save_doc: '保存圖像',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: '底部對齊',
     align_center: '居中對齊',
diff --git a/src/editor/panels/TopPanel.js b/src/editor/panels/TopPanel.js
index d01634936..34928a312 100644
--- a/src/editor/panels/TopPanel.js
+++ b/src/editor/panels/TopPanel.js
@@ -191,6 +191,7 @@ class TopPanel {
     this.hideTool("container_panel");
     this.hideTool("use_panel");
     this.hideTool("a_panel");
+    this.hideTool("xy_panel");
     if (elem) {
       const elname = elem.nodeName;
 
diff --git a/src/svgcanvas/sanitize.js b/src/svgcanvas/sanitize.js
index d3e65014b..90f68bcd4 100644
--- a/src/svgcanvas/sanitize.js
+++ b/src/svgcanvas/sanitize.js
@@ -55,7 +55,7 @@ const svgWhiteList_ = {
   stop: [ 'class', 'id', 'offset', 'requiredFeatures', 'stop-opacity', 'style', 'systemLanguage', 'stop-color', 'gradientUnits', 'gradientTransform' ],
   svg: [ 'class', 'clip-path', 'clip-rule', 'filter', 'id', 'height', 'mask', 'preserveAspectRatio', 'requiredFeatures', 'style', 'systemLanguage', 'version', 'viewBox', 'width', 'x', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'xmlns:oi', 'oi:animations', 'y', 'stroke-linejoin', 'fill-rule', 'aria-label', 'stroke-width', 'fill-rule' ],
   switch: [ 'class', 'id', 'requiredFeatures', 'systemLanguage' ],
-  symbol: [ 'class', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'opacity', 'overflow', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'viewBox' ],
+  symbol: [ 'class', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'opacity', 'overflow', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'viewBox', 'width', 'height' ],
   text: [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'transform', 'x', 'xml:space', 'y' ],
   textPath: [ 'class', 'id', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'transform', 'xlink:href' ],
   title: [],
diff --git a/src/svgcanvas/selected-elem.js b/src/svgcanvas/selected-elem.js
index 00758f8e5..28bf680ef 100644
--- a/src/svgcanvas/selected-elem.js
+++ b/src/svgcanvas/selected-elem.js
@@ -743,15 +743,17 @@ export const convertToGroup = function (elem) {
     // Not ideal, but works
     ts += ' translate(' + (pos.x || 0) + ',' + (pos.y || 0) + ')';
 
-    const prev = $elem.prev();
+    const prev = $elem.previousElementSibling;
 
     // Remove <use> element
-    batchCmd.addSubCommand(new RemoveElementCommand($elem[0], $elem[0].nextSibling, $elem[0].parentNode));
+    batchCmd.addSubCommand(new RemoveElementCommand($elem, $elem.nextElementSibling, $elem.parentNode));
     $elem.remove();
 
     // See if other elements reference this symbol
     const svgcontent = elementContext_.getSVGContent();
-    const hasMore = svgcontent.querySelectorAll('use:data(symbol)').length;
+    // const hasMore = svgcontent.querySelectorAll('use:data(symbol)').length;
+    // @todo review this logic
+    const hasMore = svgcontent.querySelectorAll('use').length;
 
     const g = elementContext_.getDOMDocument().createElementNS(NS.SVG, 'g');
     const childs = elem.childNodes;
diff --git a/src/svgcanvas/svg-exec.js b/src/svgcanvas/svg-exec.js
index a18daa436..53de4c229 100644
--- a/src/svgcanvas/svg-exec.js
+++ b/src/svgcanvas/svg-exec.js
@@ -678,31 +678,6 @@ export const embedImage = function (src) {
   });
 };
 
-/**
-* Serializes the current drawing into SVG XML text and passes it to the 'saved' handler.
-* This function also includes the XML prolog. Clients of the `SvgCanvas` bind their save
-* function to the 'saved' event.
-* @function module:svgcanvas.SvgCanvas#save
-* @param {module:svgcanvas.SaveOptions} opts
-* @fires module:svgcanvas.SvgCanvas#event:saved
-* @returns {void}
-*/
-export const save = function (opts) {
-  // remove the selected outline before serializing
-  svgCanvas.clearSelection();
-  // Update save options if provided
-  if (opts) {
-    const saveOptions = svgCanvas.mergeDeep(svgContext_.getSvgOption(), opts);
-    for (const [ key, value ] of Object.entries(saveOptions)) {
-      svgContext_.setSvgOption(key, value);
-    }
-  }
-  svgContext_.setSvgOption('apply', true);
-
-  // no need for doctype, see https://jwatt.org/svg/authoring/#doctype-declaration
-  const str = svgCanvas.svgCanvasToString();
-  svgContext_.call('saved', str);
-};
 /**
 * @typedef {PlainObject} module:svgcanvas.IssuesAndCodes
 * @property {string[]} issueCodes The locale-independent code names
diff --git a/src/svgcanvas/svgcanvas.js b/src/svgcanvas/svgcanvas.js
index f4ac97ffd..255df02a1 100644
--- a/src/svgcanvas/svgcanvas.js
+++ b/src/svgcanvas/svgcanvas.js
@@ -80,7 +80,7 @@ import {
   convertToNum, getTypeMap, init as unitsInit
 } from '../common/units.js';
 import {
-  svgCanvasToString, svgToString, setSvgString, save, exportPDF, setUseDataMethod,
+  svgCanvasToString, svgToString, setSvgString, exportPDF, setUseDataMethod,
   init as svgInit, importSvgString, embedImage, rasterExport,
   uniquifyElemsMethod, removeUnusedDefElemsMethod, convertGradientsMethod
 } from './svg-exec.js';
@@ -188,6 +188,8 @@ class SvgCanvas {
     this.$id = $id;
     this.$qq = $qq;
     this.$qa = $qa;
+    this.encode64 = encode64;
+    this.decode64 = decode64;
     this.stringToHTML = stringToHTML;
     this.insertChildAtIndex = insertChildAtIndex;
     this.getClosest = getClosest;
@@ -1365,6 +1367,8 @@ class SvgCanvas {
     /**
 * Group: Serialization.
 */
+    this.getSvgOption = () => { return saveOptions; };
+    this.setSvgOption = (key, value) => { saveOptions[key] = value; };
 
     svgInit(
       /**
@@ -1379,8 +1383,8 @@ class SvgCanvas {
         getCurrentGroup() { return currentGroup; },
         getCurConfig() { return curConfig; },
         getNsMap() { return nsMap; },
-        getSvgOption() { return saveOptions; },
-        setSvgOption(key, value) { saveOptions[key] = value; },
+        getSvgOption: this.getSvgOption,
+        setSvgOption: this.setSvgOption,
         getSvgOptionApply() { return saveOptions.apply; },
         getSvgOptionImages() { return saveOptions.images; },
         getEncodableImages(key) { return encodableImages[key]; },
@@ -1448,26 +1452,6 @@ class SvgCanvas {
       lastGoodImgUrl = val;
     };
 
-    /**
-* Does nothing by default, handled by optional widget/extension.
-* @function module:svgcanvas.SvgCanvas#open
-* @returns {void}
-*/
-    this.open = function () {
-      /* empty fn */
-    };
-
-    /**
-* Serializes the current drawing into SVG XML text and passes it to the 'saved' handler.
-* This function also includes the XML prolog. Clients of the `SvgCanvas` bind their save
-* function to the 'saved' event.
-* @function module:svgcanvas.SvgCanvas#save
-* @param {module:svgcanvas.SaveOptions} opts
-* @fires module:svgcanvas.SvgCanvas#event:saved
-* @returns {void}
-*/
-    this.save = save;
-
     /**
 * @typedef {PlainObject} module:svgcanvas.IssuesAndCodes
 * @property {string[]} issueCodes The locale-independent code names


Test patch:
diff --git a/src/editor/locale/lang.test.js b/src/editor/locale/lang.test.js
index fa606987e..45447307d 100644
--- a/src/editor/locale/lang.test.js
+++ b/src/editor/locale/lang.test.js
@@ -107,11 +107,8 @@ export default {
     largest_object: 'largest object',
     selected_objects: 'selected objects',
     smallest_object: 'smallest object',
-    new_doc: 'New Image',
-    open_doc: 'Open SVG',
     export_img: 'Export',
-    save_doc: 'Save Image',
-    import_doc: 'Import Image',
+    import_doc: 'Import SVG',
     align_to_page: 'Align Element to Page',
     align_bottom: 'Align Bottom',
     align_center: 'Align Center',