Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"dependencies": {
"@iconscout/unicons": "^4.0.1",
"@masscode/json-server": "^0.18.0",
"@sipec/vue3-tags-input": "^3.0.4",
"@vueuse/core": "^8.1.2",
"ace-builds": "^1.4.14",
"electron-store": "^8.0.1",
Expand Down
35 changes: 32 additions & 3 deletions src/main/services/api/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ import { store } from '../../store'
import { nanoid } from 'nanoid'
import { API_PORT } from '../../config'
import path from 'path'
import type { DB, Snippet, Tag } from '@shared/types/main/db'
import { remove } from 'lodash'

export const createApiServer = () => {
const db = path.resolve(store.preferences.get('storagePath') + '/db.json')
const app = jsonServer.create()
const middlewares = jsonServer.defaults()
const router = jsonServer.router(db)
const router = jsonServer.router<DB>(db)

app.use(jsonServer.bodyParser)
app.use(middlewares)

app.post('/db/update/:table', (req, res) => {
const table = req.params.table
const table = req.params.table as keyof DB
const isAllowedTable = ['folders', 'snippets', 'tags'].includes(table)

if (!isAllowedTable) {
Expand All @@ -23,7 +25,7 @@ export const createApiServer = () => {
if (req.body.value?.length === 0) {
res.status(400).send("'value' is required")
} else {
const db: any = router.db.getState()
const db = router.db.getState()

db[table] = req.body.value

Expand All @@ -34,6 +36,33 @@ export const createApiServer = () => {
}
})

app.delete('/tags/:id', (req, res) => {
const id = req.params.id
const tags = router.db.get<Tag[]>('tags').value()
const snippets = router.db
.get<Snippet[]>('snippets')
.filter(i => i.tagsIds.includes(id))
.value()
const index = tags.findIndex(i => i.id === id)

snippets.forEach(i => {
remove(i.tagsIds, item => item === id)
})

tags.splice(index, 1)
router.db.write()

res.sendStatus(200)
})

app.get('/tags/:id/snippets', (req, res) => {
const id = req.params.id
const _snippets = router.db.get<Snippet[]>('snippets').value()
const snippets = _snippets.filter(i => i.tagsIds.includes(id))

res.status(200).send(snippets)
})

app.use((req, res, next) => {
if (req.method === 'POST') {
req.body.id = nanoid(8)
Expand Down
50 changes: 47 additions & 3 deletions src/main/services/ipc/context-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,13 @@ export const subscribeToContextMenu = () => {
)

ipcMain.handle<ContextMenuPayload, ContextMenuResponse>(
'context-menu:folder',
'context-menu:library',
async (event, payload) => {
const { name, type, data } = payload

return new Promise(resolve => {
const menu = createPopupMenu([])

const createLanguageMenu = () => {
return languages.map(i => {
return {
Expand All @@ -198,7 +200,7 @@ export const subscribeToContextMenu = () => {
}) as MenuItemConstructorOptions[]
}

const menu = createPopupMenu([
const folderMenu: MenuItemConstructorOptions[] = [
{
label: 'New folder',
click: () => {
Expand Down Expand Up @@ -241,7 +243,49 @@ export const subscribeToContextMenu = () => {
label: 'Default Language',
submenu: createLanguageMenu()
}
])
]

const tagMenu: MenuItemConstructorOptions[] = [
{
label: 'Delete',
click: () => {
const buttonId = dialog.showMessageBoxSync({
message: `Are you sure you want to delete "${name}"?`,
detail:
'This will also cause all snippets to have that tag removed.',
buttons: ['Delete', 'Cancel'],
defaultId: 0,
cancelId: 1
})

if (buttonId === 0) {
resolve({
action: 'delete',
type,
data: undefined
})
} else {
resolve({
action: 'none',
type,
data: undefined
})
}
}
}
]

if (type === 'folder') {
folderMenu.forEach(i => {
menu.append(new MenuItem(i))
})
}

if (type === 'tag') {
tagMenu.forEach(i => {
menu.append(new MenuItem(i))
})
}

menu.on('menu-will-close', () => {
BrowserWindow.getFocusedWindow()?.webContents.send(
Expand Down
6 changes: 0 additions & 6 deletions src/renderer/assets/scss/variables.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
:root {
--sidebar-width: 180px;
--snippets-list-width: 250px;
--snippets-view-header-height: 60px;
--snippets-view-header-top-height: 30px;
--snippets-view-header-full-height: calc(var(--snippets-view-header-height) + var(--snippets-tags-height));
--snippets-view-footer-height: 30px;
--snippet-header-fragment-height: 25px;
--snippet-tab-height: 30px;
--title-bar-height: 15px;
--menu-header: 80px;

Expand Down
29 changes: 9 additions & 20 deletions src/renderer/components/editor/TheEditor.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<template>
<div
class="editor"
:class="{ 'with-fragments': fragments }"
>
<div class="editor">
<div
ref="editorRef"
class="main"
Expand Down Expand Up @@ -36,7 +33,8 @@ import type { Ace } from 'ace-builds'
import ace from 'ace-builds'
import './module-resolver'
import type { Language } from '@shared/types/renderer/editor'
import { languages, oldLanguageMap } from './languages'
import { languages } from './languages'
import { useAppStore } from '@/store/app'

interface Props {
lang: Language
Expand All @@ -52,12 +50,13 @@ interface Emits {

const props = withDefaults(defineProps<Props>(), {
lang: 'typescript',
theme: 'chrome',
fragments: false
theme: 'chrome'
})

const emit = defineEmits<Emits>()

const appStore = useAppStore()

const editorRef = ref()
const cursorPosition = reactive({
row: 0,
Expand All @@ -70,6 +69,8 @@ const localLang = computed({
set: v => emit('update:lang', v)
})

const footerHeight = appStore.sizes.editor.footerHeight + 'px'

const init = async () => {
editor = ace.edit(editorRef.value, {
theme: `ace/theme/${props.theme}`,
Expand Down Expand Up @@ -141,20 +142,8 @@ watch(
<style lang="scss" scoped>
.editor {
padding-top: 4px;
&.with-fragments {
.main {
height: calc(
100vh - var(--title-bar-height) - var(--snippets-view-header-top-height) -
var(--snippets-view-footer-height) -
var(--snippet-header-fragment-height)
);
}
}
.main {
height: calc(
100vh - var(--title-bar-height) - var(--snippets-view-header-top-height) -
var(--snippets-view-footer-height)
);
height: calc(100% - v-bind(footerHeight));
}
.footer {
height: 30px;
Expand Down
54 changes: 46 additions & 8 deletions src/renderer/components/sidebar/SidebarListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
:class="{
'is-selected': isSelected,
'is-focused': isFocused,
'is-system': system
'is-system': isSystem,
'is-tag': isTag
}"
@click="isFocused = true"
@contextmenu="onClickContextMenu"
Expand All @@ -32,38 +33,46 @@ import { onClickOutside } from '@vueuse/core'
import { ipc } from '@/electron'
import type {
ContextMenuPayload,
ContextMenuResponse
ContextMenuResponse,
ContextMenuType
} from '@shared/types/main'
import { useFolderStore } from '@/store/folders'
import { useTagStore } from '@/store/tags'
import type { FolderTree } from '@shared/types/main/db'
import { useSnippetStore } from '@/store/snippets'

interface Props {
id?: string
icon?: FunctionalComponent
system?: boolean
isSystem?: boolean
nested?: boolean
open?: boolean
isSelected?: boolean
isTag?: boolean
isFolder?: boolean
model?: FolderTree
alias?: ContextMenuType
name?: string
}

const props = withDefaults(defineProps<Props>(), {
system: false,
nested: false,
open: false
})

const folderStore = useFolderStore()
const snippetStore = useSnippetStore()
const tagStore = useTagStore()

const isFocused = ref(false)
const itemRef = ref()

const onClickContextMenu = async () => {
if (props.id) {
if (props.isFolder) {
const { action, type, data } = await ipc.invoke<
ContextMenuResponse,
ContextMenuPayload
>('context-menu:folder', {
>('context-menu:library', {
name: props.model?.name,
type: 'folder',
data: JSON.parse(JSON.stringify(props.model))
Expand All @@ -80,7 +89,35 @@ const onClickContextMenu = async () => {

if (action === 'update:language') {
console.log(data)
await folderStore.patchFoldersById(props.id, { defaultLanguage: data })
await folderStore.patchFoldersById(props.id!, { defaultLanguage: data })
}
}

if (props.isTag) {
const { action, type, data } = await ipc.invoke<
ContextMenuResponse,
ContextMenuPayload
>('context-menu:library', {
name: props.name,
type: 'tag',
data: {}
})

if (action === 'delete') {
await tagStore.deleteTagById(props.id!)

if (props.id === tagStore.selectedId) {
tagStore.selectedId = undefined
}

if (tagStore.tags.length) {
const firstTagId = tagStore.tags[0].id
tagStore.selectedId = firstTagId
snippetStore.setSnippetsByTagId(firstTagId)
} else {
snippetStore.selected = undefined
snippetStore.snippets = []
}
}
}
}
Expand All @@ -99,7 +136,8 @@ onClickOutside(itemRef, () => {
width: 100%;
z-index: 2;
user-select: none;
&.is-system {
&.is-system,
&.is-tag {
padding-left: var(--spacing-sm);
padding-right: var(--spacing-sm);
}
Expand Down
Loading