Skip to content

Commit d1fe23f

Browse files
feat: add editor preferences (#18)
1 parent c486fbf commit d1fe23f

File tree

15 files changed

+288
-15
lines changed

15 files changed

+288
-15
lines changed

src/main/store/module/preferences.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ export default new Store<PreferencesStore>({
1313

1414
defaults: {
1515
storagePath: defaultPath,
16-
backupPath
16+
backupPath,
17+
editor: {
18+
wrap: 'free',
19+
fontFamily: 'SF Mono, Consolas, Menlo',
20+
fontSize: 12,
21+
showInvisibles: false,
22+
tabSize: 2
23+
}
1724
}
1825
})

src/renderer/App.vue

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<script setup lang="ts">
1717
import router from '@/router'
1818
import { ref, watch } from 'vue'
19-
import { ipc } from './electron'
19+
import { ipc, store } from './electron'
2020
import { useAppStore } from './store/app'
2121
import { repository } from '../../package.json'
2222
import { useSnippetStore } from './store/snippets'
@@ -31,6 +31,13 @@ const snippetStore = useSnippetStore()
3131
3232
const isUpdateAvailable = ref(false)
3333
34+
const init = () => {
35+
const isValid = appStore.isEditorSettingsValid(
36+
store.preferences.get('editor')
37+
)
38+
if (isValid) appStore.editor = store.preferences.get('editor')
39+
}
40+
3441
const setTheme = (theme: string) => {
3542
document.body.dataset.theme = theme
3643
}
@@ -39,6 +46,8 @@ const onClickUpdate = () => {
3946
ipc.invoke('main:open-url', `${repository}/releases`)
4047
}
4148
49+
init()
50+
4251
watch(
4352
() => appStore.theme,
4453
() => setTheme(appStore.theme),

src/renderer/assets/scss/ace.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
.ace_editor {
2+
::-webkit-scrollbar {
3+
width: 6px;
4+
height: 6px;
5+
}
6+
::-webkit-scrollbar-thumb {
7+
background-color: var(--color-editor-scrollbar);
8+
border-radius: 6px;
9+
}
10+
}
111
.ace_gutter {
212
width: 40px !important;
313
background: transparent !important;
@@ -7,4 +17,8 @@
717
}
818
.ace_scroller {
919
left: 40px !important;
20+
&.ace_scroll-left {
21+
box-shadow: none;
22+
border-left: 1px solid var(--color-border);
23+
}
1024
}

src/renderer/assets/scss/variables.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
--font-primary: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
1616
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
17-
--font-code: Menlo, Monaco, "Courier New", monospace;
17+
--font-code: 'SF Mono', 'Consolas', Menlo, Monaco, "Courier New", monospace;
1818

1919
--text-base-size: 10px;
2020
--text-xs: calc(var(--text-base-size) * 1);
@@ -43,6 +43,8 @@
4343

4444
--color-sidebar-item-selected: var(--color-contrast-low);
4545
--color-snippet-selected: hsl(0, 0%, 94%);
46+
47+
--color-editor-scrollbar: rgba(121, 121, 121, 0.4);
4648
}
4749

4850
[data-theme='dark'] {

src/renderer/components/editor/TheEditor.vue

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,12 @@ const init = async () => {
103103
editor = ace.edit(editorRef.value, {
104104
theme: `ace/theme/${props.theme}`,
105105
useWorker: false,
106-
fontSize: 12,
106+
fontSize: appStore.editor.fontSize,
107+
fontFamily: appStore.editor.fontFamily,
107108
printMargin: false,
108-
tabSize: 2
109+
tabSize: appStore.editor.tabSize,
110+
wrap: appStore.editor.wrap,
111+
showInvisibles: appStore.editor.showInvisibles
109112
})
110113
111114
setValue()
@@ -153,6 +156,12 @@ const setLang = () => {
153156
const setTheme = () => {
154157
editor.session.setMode(`ace/theme/${props.theme}`)
155158
}
159+
const setOption = <T extends keyof Ace.EditSessionOptions>(
160+
name: T,
161+
value: Ace.EditSessionOptions[T]
162+
) => {
163+
editor.session.setOption(name, value)
164+
}
156165
157166
const resetUndoStack = () => {
158167
editor.getSession().setUndoManager(new ace.UndoManager())
@@ -204,6 +213,10 @@ watch(
204213
}
205214
)
206215
216+
appStore.$subscribe(mutation => {
217+
console.log(mutation)
218+
})
219+
207220
window.addEventListener('resize', () => {
208221
forceRefresh.value = Math.random()
209222
})
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<template>
2+
<div class="editor-preferences">
3+
<AppForm>
4+
<AppFormItem label="Font Size">
5+
<AppInput
6+
v-model="appStore.editor.fontSize"
7+
type="number"
8+
style="width: 100px"
9+
/>
10+
</AppFormItem>
11+
<AppFormItem label="Font Family">
12+
<AppInput v-model="appStore.editor.fontFamily" />
13+
</AppFormItem>
14+
<AppFormItem label="Wrap">
15+
<AppSelect
16+
v-model="appStore.editor.wrap"
17+
:options="wrapOptions"
18+
/>
19+
</AppFormItem>
20+
<AppFormItem label="Tab Size">
21+
<AppInput
22+
v-model="appStore.editor.tabSize"
23+
type="number"
24+
style="width: 100px"
25+
/>
26+
</AppFormItem>
27+
<AppFormItem label="Show Invisibles">
28+
<AppCheckbox
29+
v-model="appStore.editor.showInvisibles"
30+
name="showInvisibles"
31+
/>
32+
</AppFormItem>
33+
</AppForm>
34+
</div>
35+
</template>
36+
37+
<script setup lang="ts">
38+
import { store } from '@/electron'
39+
import { useAppStore } from '@/store/app'
40+
import { watch } from 'vue'
41+
42+
const appStore = useAppStore()
43+
44+
const wrapOptions = [
45+
{ label: 'Word Wrap', value: 'free' },
46+
{ label: 'Off', value: 'off' }
47+
]
48+
49+
watch(
50+
() => appStore.editor,
51+
v => {
52+
store.preferences.set('editor', { ...v })
53+
},
54+
{ deep: true }
55+
)
56+
</script>
57+
58+
<style lang="scss" scoped></style>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<template>
2+
<div class="app-checkbox">
3+
<label>
4+
<input
5+
:id="'id-' + Math.random()"
6+
type="checkbox"
7+
:name="name"
8+
_value="localValue"
9+
@change="onChange"
10+
>
11+
<div class="inner">
12+
<UniconsCheck v-if="modelValue" />
13+
</div>
14+
<div
15+
v-if="label"
16+
class="label"
17+
>
18+
{{ label }}
19+
</div>
20+
</label>
21+
</div>
22+
</template>
23+
24+
<script setup lang="ts">
25+
interface Props {
26+
name: string
27+
label?: string
28+
modelValue: boolean
29+
}
30+
31+
interface Emits {
32+
(e: 'update:modelValue', value: boolean): void
33+
}
34+
35+
const emit = defineEmits<Emits>()
36+
37+
const props = defineProps<Props>()
38+
39+
const onChange = () => {
40+
emit('update:modelValue', !props.modelValue)
41+
}
42+
</script>
43+
44+
<style lang="scss" scoped>
45+
.app-checkbox {
46+
input {
47+
display: none;
48+
}
49+
.inner {
50+
width: 16px;
51+
height: 16px;
52+
border: 1px solid var(--color-border);
53+
border-radius: 3px;
54+
display: flex;
55+
align-items: center;
56+
:deep(svg) {
57+
position: relative;
58+
}
59+
}
60+
}
61+
</style>

src/renderer/components/ui/AppInput.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
import { computed } from 'vue'
1212
1313
interface Props {
14-
modelValue: string
14+
modelValue: string | number
1515
}
1616
1717
interface Emits {
18-
(e: 'update:modelValue', value: string): void
18+
(e: 'update:modelValue', value: string | number): void
1919
}
2020
2121
const emit = defineEmits<Emits>()
@@ -32,10 +32,11 @@ const localValue = computed({
3232

3333
<style lang="scss" scoped>
3434
.input {
35-
min-width: 300px;
35+
width: 300px;
3636
height: 32px;
3737
outline: none;
3838
border: 1px solid var(--color-border);
3939
border-radius: 3px;
40+
padding: 0 var(--spacing-xs);
4041
}
4142
</style>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<template>
2+
<div class="app-select">
3+
<select
4+
v-model="localValue"
5+
class="inner"
6+
v-bind="$attrs"
7+
>
8+
<option
9+
v-for="i in options"
10+
:key="i.value"
11+
:value="i.value"
12+
>
13+
{{ i.label }}
14+
</option>
15+
</select>
16+
<UniconsAngleDown />
17+
</div>
18+
</template>
19+
20+
<script setup lang="ts">
21+
import { computed } from 'vue'
22+
23+
interface Props {
24+
options: { label: string; value: string }[]
25+
modelValue: any
26+
}
27+
28+
interface Emits {
29+
(e: 'update:modelValue', value: any): void
30+
}
31+
32+
const emit = defineEmits<Emits>()
33+
34+
const props = defineProps<Props>()
35+
36+
const localValue = computed({
37+
get: () => props.modelValue,
38+
set: v => emit('update:modelValue', v)
39+
})
40+
</script>
41+
42+
<style lang="scss" scoped>
43+
.app-select {
44+
position: relative;
45+
.inner {
46+
height: 32px;
47+
outline: none;
48+
border: 1px solid var(--color-border);
49+
border-radius: 3px;
50+
padding: 0 var(--spacing-xs);
51+
-webkit-appearance: none;
52+
width: 300px;
53+
}
54+
:deep(svg) {
55+
position: absolute;
56+
right: 2px;
57+
top: 50%;
58+
transform: translateY(-50%);
59+
}
60+
}
61+
</style>

src/renderer/components/ui/form/AppForm.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<style lang="scss" scoped>
1010
.form {
11-
width: 650px;
11+
width: 700px;
1212
display: flex;
1313
flex-flow: column;
1414
gap: var(--spacing-md);

0 commit comments

Comments
 (0)