@setoelkahfi / svara / commits / 583590b

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