Skip to content
Merged
6 changes: 6 additions & 0 deletions config/electron-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ export default {
extraMetadata: {
main: 'src/main/index.js'
},
protocols: [
{
name: 'massCode',
schemes: ['masscode']
}
],
files: [
'!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}',
'!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}',
Expand Down
36 changes: 35 additions & 1 deletion src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,24 @@ import { checkForUpdateWithInterval } from './services/update-check'

const isDev = process.env.NODE_ENV === 'development'
const isMac = process.platform === 'darwin'
const gotTheLock = app.requestSingleInstanceLock()

let mainWindow: BrowserWindow

createDb()
const apiServer = new ApiServer()

subscribeToChannels()
subscribeToDialog()

if (!gotTheLock) {
// @ts-ignore
return app.quit()
}

function createWindow () {
const bounds = store.app.get('bounds')
const mainWindow = new BrowserWindow({
mainWindow = new BrowserWindow({
width: 1000,
height: 600,
...bounds,
Expand Down Expand Up @@ -54,6 +62,16 @@ const storeBounds = debounce((mainWindow: BrowserWindow) => {
store.app.set('bounds', mainWindow.getBounds())
}, 300)

if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient('masscode', process.execPath, [
path.resolve(process.argv[1])
])
}
} else {
app.setAsDefaultProtocolClient('masscode')
}

app.whenReady().then(async () => {
createWindow()

Expand All @@ -74,6 +92,22 @@ app.on('browser-window-focus', () => {
BrowserWindow.getFocusedWindow()?.webContents.send('main:focus')
})

app.on('second-instance', (e, argv) => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
}

if (process.platform !== 'darwin') {
const url = argv.find(i => i.startsWith('masscode://'))
BrowserWindow.getFocusedWindow()?.webContents.send('main:app-protocol', url)
}
})

app.on('open-url', (event, url) => {
BrowserWindow.getFocusedWindow()?.webContents.send('main:app-protocol', url)
})

ipcMain.handle('main:restart-api', () => {
apiServer.restart()
})
Expand Down
6 changes: 4 additions & 2 deletions src/main/services/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ export const track = (event: TrackEvents, payload?: string) => {
? `${version}/${os}/${event}/${payload}`
: `${version}/${os}/${event}`

if (isDev && process.env.DEBUG?.includes('analytics')) {
console.log('[analytics]:', path)
if (isDev) {
if (process.env.DEBUG?.includes('analytics')) {
console.log('[analytics]:', path)
}
} else {
analytics.pageview(path).send()
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/services/api/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ export class ApiServer {
}
}

// Почему то на windows API сервер падает, не смотря на то,
// что порт API свободен, поэтому просто обрабатываем ошибку
server.on('error', err => console.error(err))

return server
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/services/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@
"show": "Show",
"collapse-all": "Collapse All",
"expand-all": "Expand All",
"restore": "Restore"
"restore": "Restore",
"copy-snippet-link": "Copy Snippet Link"
}
3 changes: 2 additions & 1 deletion src/main/services/i18n/locales/ru/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@
"show": "Show",
"collapse-all": "Закрыть все",
"expand-all": "Открыть все",
"restore": "Восстановить"
"restore": "Восстановить",
"copy-snippet-link": "Скопировать ссылку"
}
11 changes: 11 additions & 0 deletions src/main/services/ipc/context-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ export const subscribeToContextMenu = () => {
}
},
{ type: 'separator' },
{
label: i18n.t('copy-snippet-link'),
click: () => {
resolve({
action: 'copy-snippet-link',
type,
data: true
})
}
},
{ type: 'separator' },
{
label: i18n.t('duplicate'),
click: () => {
Expand Down
10 changes: 9 additions & 1 deletion src/renderer/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ import {
onCopySnippet,
emitter,
onCreateSnippet,
onAddDescription
onAddDescription,
goToSnippet
} from '@/composable'
import { useRoute } from 'vue-router'
import type { Snippet } from '@shared/types/main/db'
Expand Down Expand Up @@ -164,6 +165,13 @@ ipc.on('main:focus', () => {
showSupportToast()
})

ipc.on('main:app-protocol', (event, payload: string) => {
if (/^masscode:\/\/snippets/.test(payload)) {
const snippetId = payload.split('/').pop()
if (snippetId) goToSnippet(snippetId)
}
})

ipc.on('main-menu:preferences', () => {
router.push('/preferences')
})
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/assets/scss/themes.scss
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@
}

[data-theme^="dark"] {
a {
> a {
color: var(--color-contrast-medium);
&:hover {
color: var(--color-text);
Expand Down
28 changes: 20 additions & 8 deletions src/renderer/components/markdown/TheMarkdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ import { computed, onBeforeUnmount, onMounted, ref, watch, nextTick } from 'vue'
import { ipc, store } from '@/electron'
import { marked } from 'marked'
import mermaid from 'mermaid'
import { useHljsTheme } from '@/composable'
import { useHljsTheme, goToSnippet } from '@/composable'
import { useCodemirror } from '@/composable/codemirror'

import { nanoid } from 'nanoid'

const isDev = import.meta.env.DEV
Expand Down Expand Up @@ -75,7 +74,12 @@ const init = () => {
}
},
link (href: string, title: string, text: string) {
return `<a href="${href}" class="external">${text}</a>`
if (/^masscode:\/\/snippets/.test(href)) {
const id = href.split('/').pop()
return `<a href="${href}" class="snippet-link" data-snippet-id="${id}">${text}</a>`
} else {
return `<a href="${href}" class="external">${text}</a>`
}
}
}

Expand Down Expand Up @@ -181,9 +185,11 @@ const render = () => {
'type',
'checked',
'disabled',
'id'
'id',
'data-*'
]
}
},
allowedSchemes: ['http', 'https', 'masscode']
})

const re = /src="\.\//g
Expand All @@ -196,12 +202,18 @@ const render = () => {
renderedHtml.value = html
}

const openExternal = (e: Event) => {
const onLink = async (e: Event) => {
const el = e.target as HTMLAnchorElement
e.preventDefault()

if (el.classList.contains('external')) {
ipc.invoke('main:open-url', el.href)
}

if (el.classList.contains('snippet-link')) {
const { snippetId } = el.dataset
if (snippetId) goToSnippet(snippetId)
}
}

const height = computed(() => {
Expand Down Expand Up @@ -257,11 +269,11 @@ watch(
init()

onMounted(() => {
document.addEventListener('click', openExternal)
document.addEventListener('click', onLink)
})

onBeforeUnmount(() => {
document.removeEventListener('click', openExternal)
document.removeEventListener('click', onLink)
})

window.addEventListener('resize', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/sidebar/SidebarList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ emitter.on('scroll-to:folder', id => {
nextTick(() => {
const el = document.querySelector<HTMLElement>(`[data-id='${id}']`)
if (el) {
setScrollPosition(bodyRef.value!, el.getBoundingClientRect().top + 210)
setScrollPosition(bodyRef.value!, el.getBoundingClientRect().top - 210)
}
})
})
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/components/sidebar/SidebarListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { useTagStore } from '@/store/tags'
import type { FolderTree } from '@shared/types/main/db'
import { useSnippetStore } from '@/store/snippets'
import { emitter } from '@/composable'
import { onScrollToFolder } from './composable'

interface Props {
id?: string
Expand Down Expand Up @@ -162,6 +163,8 @@ onClickOutside(itemRef, () => {
isFocused.value = false
})

onScrollToFolder(isFocused)

watch(
() => props.isSelected,
v => {
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/components/sidebar/TheSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
v-model="folderStore.foldersTree"
:selected-id="folderStore.selectedId"
:context-menu-handler="contextMenuHandler"
:focus-handler="focusHandler"
@update:model-value="onUpdate"
@click:node="onClickFolder"
>
Expand Down Expand Up @@ -81,6 +82,7 @@
</template>

<script setup lang="ts">
import type { Ref } from 'vue'
import { onMounted, ref, watch } from 'vue'
import type {
SidebarSystemFolder,
Expand All @@ -101,6 +103,7 @@ import { emitter, onAddNewFolder } from '@/composable'
import interact from 'interactjs'
import { useAppStore } from '@/store/app'
import type { Snippet } from '@shared/types/main/db'
import { onScrollToFolder } from './composable'

const folderStore = useFolderStore()
const snippetStore = useSnippetStore()
Expand Down Expand Up @@ -184,6 +187,10 @@ const onDragEnter = (id: string) => {
folderStore.hoveredId = id
}

const focusHandler = (isFocused: Ref) => {
onScrollToFolder(isFocused)
}

onMounted(() => {
interact(sidebarRef.value).resizable({
allowFrom: gutterRef.value,
Expand Down
17 changes: 17 additions & 0 deletions src/renderer/components/sidebar/composable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { emitter } from '@/composable'
import { onUnmounted } from 'vue'
import type { Ref } from 'vue'

export const onScrollToFolder = (isFocused: Ref) => {
if (!isFocused) return

const handler = () => {
isFocused.value = false
}

emitter.on('scroll-to:folder', handler)

onUnmounted(() => {
emitter.off('scroll-to:folder', handler)
})
}
23 changes: 21 additions & 2 deletions src/renderer/components/snippets/SnippetListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ import type {
ContextMenuRequest,
ContextMenuResponse
} from '@shared/types/main'
import { onClickOutside } from '@vueuse/core'
import { computed, ref } from 'vue'
import { onClickOutside, useClipboard } from '@vueuse/core'
import { computed, onUnmounted, ref } from 'vue'
import type { SystemFolderAlias } from '@shared/types/renderer/sidebar'
import { useTagStore } from '@/store/tags'
import { isToday, format } from 'date-fns'
import { emitter } from '@/composable'

interface Props {
id: string
Expand Down Expand Up @@ -225,6 +226,12 @@ const onClickContextMenu = async () => {
track('snippets/restore-from-trash')
}

if (action === 'copy-snippet-link') {
const { copy } = useClipboard({ source: `masscode://snippets/${props.id}` })
copy()
track('snippets/copy-link')
}

isHighlighted.value = false
isFocused.value = false
snippetStore.isContextState = false
Expand Down Expand Up @@ -274,6 +281,18 @@ const onDragStart = (e: DragEvent) => {
const onDragEnd = () => {
folderStore.hoveredId = ''
}

const onScrollToSnippet = () => {
if (snippetStore.selectedId !== props.id) {
isFocused.value = false
}
}

emitter.on('scroll-to:snippet', onScrollToSnippet)

onUnmounted(() => {
emitter.off('scroll-to:snippet', onScrollToSnippet)
})
</script>

<style lang="scss" scoped>
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/components/ui/AppTree/AppTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
</template>

<script setup lang="ts">
import type { Ref } from 'vue'
import { provide, ref, watch } from 'vue'
import { store } from './composable'
import type { Node } from './types'
Expand All @@ -37,6 +38,7 @@ interface Props {
createGhostEl?: Function
// Колбек должен вернуть состояние для props.isHighlighted AppTreeNode
contextMenuHandler: () => Promise<boolean>
focusHandler?: (isFocused: Ref) => void
}

interface Emits {
Expand Down Expand Up @@ -68,6 +70,7 @@ provide('updateValue', updateValue)
provide('clickNode', clickNode)
provide('contextMenuHandler', props.contextMenuHandler)
provide('isHoveredByIdDisabled', isHoveredByIdDisabled)
provide('focusHandler', props.focusHandler)

defineExpose({
setHoveredNodeId
Expand Down
Loading