feat: added ability to save files
mellbacon committed
May 19, 2023 at 19:53 UTC
583590b21a4cbb00b0bd1d7d6cb52019c2828f93
7 files changed
+100
-14
src/lib/Editor.svelte
+38
-2
@@ -7,12 +7,24 @@
7
import {defaultKeymap, history, historyKeymap, indentWithTab} from "@codemirror/commands"
8
import {searchKeymap} from "@codemirror/search"
9
import { onMount, tick } from "svelte";
10
+ import { writable } from "svelte/store";
11
+ import { updateSaveState } from "./File";
12
13
let ref;
12
- let editorView;
14
+ let editorView: EditorView;
15
export let hidden = false;
16
export let content = "";
17
18
+ export function updateFileInfo(file) {
19
+ updateFile(file);
20
+ }
21
+ export function getFileInfo() {
22
+ return $file_info;
23
+ }
24
+ export function getFileContent() {
25
+ return content;
26
+ }
27
+
28
onMount(() => {
29
editorView = new EditorView({
30
parent: ref,
@@ -40,9 +52,33 @@
52
})
53
})
54
});
55
+ async function updateContent() {
56
+ await tick();
57
+ content = editorView.state.doc.toString();
58
+ updateSaveState(false);
59
+ }
60
+ export async function focus() {
61
+ // takes two ticks to focus for some reason
62
+ await tick();
63
+ await tick();
64
+ editorView.focus();
65
+ //await updateContent();
66
+ }
67
+</script>
68
+<script lang="ts" context="module">
69
+ export const file_info = writable({
70
+ "filename": "",
71
+ "path": "",
72
+ "fileType": "",
73
+ "readonly": false,
74
+ });
75
+
76
+ export function updateFile(fileinfo) {
77
+ file_info.set(fileinfo);
78
+ }
79
</script>
80
45
-<div bind:this={ref} class="editor" class:hidden></div>
81
+<div bind:this={ref} class="editor" class:hidden on:input={updateContent}></div>
82
83
<style lang="scss">
84
.editor {
src/lib/EditorTabList.svelte
+4
-1
@@ -28,6 +28,9 @@
28
tab.content.updateTheme();
29
}
30
}
31
+ setActive(id) {
32
+ editorTab.setActive(id);
33
+ }
34
}
35
36
const editorTab = new Tab(EditorTab);
@@ -44,7 +47,7 @@
47
48
export let hidden = editorTab.hidden;
49
export let isfile = editorTab.isfile;
47
- let tabs = editorTab.tabs;
50
+ export let tabs = editorTab.tabs;
51
</script>
52
<div id="editor-tabs" class:hidden={$hidden}>
53
<TabList tabs={tabs} on:closetab={(e) => {closeTab(e.detail.tabid)}} on:select={(e) => {editorTab.setActive(e.detail.tabid)}}></TabList>
src/lib/File.ts
new
+30
@@ -0,0 +1,30 @@
1
+import { dialog, fs, path } from "@tauri-apps/api";
2
+import { get } from 'svelte/store';
3
+import { tabs } from "./EditorTabList.svelte";
4
+
5
+export async function saveFile(saveAs = false) {
6
+ const tab = get(tabs).find(t => t.active && t.isfile);
7
+ if (!tab.path.includes(path.sep) || tab.path === "" || saveAs) {
8
+ let newPath = await dialog.save({defaultPath: `${tab.label}.txt`});
9
+ if (newPath === null) return;
10
+
11
+ tab.path = newPath;
12
+ tab.label = newPath.split(path.sep).pop();
13
+ }
14
+ // write changes to the file
15
+ fs.writeFile(tab.path, tab.content.getFileContent());
16
+ tab.content.updateFileInfo({
17
+ "filename": tab.label,
18
+ "path": tab.path,
19
+ "fileType": await path.extname(tab.path),
20
+ "readonly": false,
21
+ });
22
+ tab.setActive(tab.id);
23
+}
24
+
25
+export function updateSaveState(saved = true) {
26
+ if (saved) return; // prevents this being fired on every state check
27
+ const tab = get(tabs).find(t => t.active && t.isfile);
28
+ tab.saved = false;
29
+ tab.setActive(tab.id);
30
+}
\ No newline at end of file
src/lib/Header.svelte
+3
-2
@@ -3,13 +3,14 @@
3
import Dropdown from "./utility/Dropdown.svelte";
4
import Settings from "carbon-icons-svelte/lib/Settings.svelte";
5
import { addTab, addEditorTab } from "../lib/EditorTabList.svelte";
6
+ import { saveFile } from "./File";
7
8
const items = [
9
{menuname: "File", children: [
10
{name: "New File", shortcut: "Ctrl + N", action: () => {addEditorTab()}},
11
{name: "Open File...", shortcut: "Ctrl + O", action: () => {console.log("click")}},
11
- {name: "Save File", shortcut: "Ctrl + S", action: () => {console.log("click")}},
12
- {name: "Save File As...", shortcut: "Ctrl + Shift + S", action: () => {console.log("click")}},
12
+ {name: "Save File", shortcut: "Ctrl + S", action: async () => {await saveFile()}},
13
+ {name: "Save File As...", shortcut: "Ctrl + Shift + S", action: async () => {await saveFile(true)}},
14
{name: "Close Tab", disabled: true, shortcut: "", action: () => {console.log("click")}},
15
]},
16
{menuname: "Edit", children: [
src/lib/Tab/Tab.svelte
+7
-6
@@ -6,6 +6,7 @@
6
export let label = "Untitled-1";
7
export let path = "";
8
export let active = false;
9
+ export let saved = true;
10
11
let tab = null;
12
@@ -18,12 +19,9 @@
19
</script>
20
<div bind:this={tab} title={path} id={`editorTab-${id}`} class="tab" class:active={active}>
21
<!-- svelte-ignore a11y-click-events-have-key-events -->
21
- <div class="tab-content" on:mousedown = {
22
- (e) => {
23
- if (e.button === 0) handleSelect(id);
24
- }
25
- }>
22
+ <div class="tab-content" on:mousedown = {(e) => { if (e.button === 0) handleSelect(id);}}>
23
<span class="tab-label">{label}</span>
24
+ <span class="save-state" class:saved></span>
25
</div>
26
<!-- svelte-ignore a11y-click-events-have-key-events -->
27
<div class="close-tab">
@@ -58,7 +56,10 @@
56
overflow-y: hidden;
57
height: 100%;
58
padding-left: 2px;
61
- :nth-child(2) {
59
+ .save-state {
60
+ &.saved {
61
+ display: none !important;
62
+ }
63
display: block;
64
width: 6px;
65
height: 6px;
src/lib/Tab/Tab.ts
+17
-2
@@ -1,5 +1,6 @@
1
import { writable } from "svelte/store";
2
import Editor from "../Editor.svelte";
3
+import { path as p } from "@tauri-apps/api";
4
5
export class Tab {
6
id = 0;
@@ -20,6 +21,7 @@ export class Tab {
21
tab.active = true;
22
if (tab.isfile) {
23
this.isfile.set(true);
24
+ tab.content.focus();
25
}
26
else {
27
this.isfile.set(false);
@@ -61,14 +63,27 @@ export class Tab {
63
64
this.updateTabs();
65
}
64
- addEditorTab(path: string, label: string = "") {
66
+ async addEditorTab(path: string, label: string = "") {
67
if (this.tabOpen(path)) {
68
return;
69
}
70
let content = new Editor({target: document.getElementById("tabview"), props: {content: ""}});
69
-
71
let tab = new this.Tab(this.id, label, content, path);
72
tab.isfile = true;
73
+ tab.saved = true;
74
+ let fileType = "";
75
+ try {
76
+ fileType = await p.extname(tab.path);
77
+ } catch (error) {
78
+ console.warn("Cannot find file extension. Setting to empty string.")
79
+ fileType = "";
80
+ }
81
+ content.updateFileInfo({
82
+ "filename": tab.label,
83
+ "path": tab.path,
84
+ "fileType": fileType,
85
+ "readonly": false,
86
+ });
87
this.tablist = [...this.tablist, tab];
88
89
this.updateView();
src/lib/Tab/TabList.svelte
+1
-1
@@ -38,7 +38,7 @@
38
</script>
39
<div bind:this={tabcontainer} id="tablist">
40
{#each $tabs as tab}
41
- <Tab on:closetab on:select id={tab.id} label={tab.label} path={tab.path} active={tab.active} />
41
+ <Tab on:closetab on:select id={tab.id} label={tab.label} path={tab.path} active={tab.active} saved={tab.saved} />
42
{/each}
43
</div>
44