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 src/main/i18n/locales/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,4 @@ fix(i18n): pt_BR translation
2. Consider the context in which the phrase is used
3. Maintain a consistent style and terminology throughout the translation
4. Verify that your translation fits the UI components (text length, line breaks)
5. For narrow navigation areas such as the space rail, keep product labels short and stable. If a locale would make the label too long, it is acceptable to omit that locale-specific label and rely on the `en_US` fallback while adding localized tooltip keys instead.
9 changes: 9 additions & 0 deletions src/main/i18n/locales/en_US/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@
"copied": "Copied",
"currencyUnavailable": "Currency rates service unavailable"
},
"spaces": {
"label": "Spaces",
"code": "Code",
"tools": "Tools",
"math": "Math",
"codeTooltip": "Code snippets",
"toolsTooltip": "Developer tools",
"mathTooltip": "Math notebook"
},
"loading": "App loading...",
"placeholder": {
"emptyTagList": "Add tags to snippets to see them here",
Expand Down
16 changes: 16 additions & 0 deletions src/main/i18n/locales/ru_RU/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,26 @@
"selectedMultiple": "Выбрано сниппетов: {{count}}",
"noSelected": "Нет выбранных сниппетов"
},
"mathNotebook": {
"label": "Математический блокнот",
"sheetList": "Список листов",
"newSheet": "Новый лист",
"untitled": "Без названия",
"copied": "Скопировано",
"currencyUnavailable": "Сервис курсов валют недоступен"
},
"spaces": {
"label": "Пространства",
"codeTooltip": "Сниппеты кода",
"toolsTooltip": "Инструменты разработчика",
"mathTooltip": "Математический блокнот"
},
"loading": "Загрузка приложения...",
"placeholder": {
"emptyTagList": "Добавьте теги к сниппетам, чтобы увидеть их здесь",
"emptyFoldersList": "Нет папок",
"emptySnippetsList": "Нет сниппетов",
"emptySheetList": "Нет листов",
"search": "Поиск",
"addTag": "Добавить тег"
}
Expand Down
19 changes: 11 additions & 8 deletions src/renderer/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { useApp, useTheme } from '@/composables'
import { i18n, ipc } from '@/electron'
import { RouterName } from '@/router'
import { isSpaceRouteName } from '@/spaceDefinitions'
import { LoaderCircle } from 'lucide-vue-next'
import { loadWASM } from 'onigasm'
import onigasmFile from 'onigasm/lib/onigasm.wasm?url'
Expand All @@ -11,7 +12,7 @@ import { loadGrammars } from './components/editor/grammars'
import { registerIPCListeners } from './ipc'
import { notifications } from './services/notifications'

const { isSponsored, isAppLoading } = useApp()
const { isAppLoading } = useApp()
const route = useRoute()

const showLoader = ref(false)
Expand Down Expand Up @@ -64,13 +65,15 @@ init()
data-title-bar
class="absolute top-0 z-50 h-[var(--title-bar-height)] w-full select-none"
/>
<div
v-if="!isSponsored"
class="text-text-muted absolute top-1 right-2 z-50 text-[11px] uppercase"
>
{{ i18n.t("messages:special.unsponsored") }}
</div>
<RouterView />
<RouterView v-slot="{ Component, route: currentRoute }">
<AppSpaceShell v-if="isSpaceRouteName(currentRoute.name)">
<component :is="Component" />
</AppSpaceShell>
<component
:is="Component"
v-else
/>
</RouterView>
<div
v-if="isLoaderVisible"
class="bg-bg absolute inset-0 z-50 flex flex-col items-center justify-center"
Expand Down
10 changes: 10 additions & 0 deletions src/renderer/components/app-space-shell/AppSpaceShell.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<template>
<div class="grid h-screen grid-cols-[72px_1fr] overflow-hidden">
<div class="bg-bg border-border/70 mt-2 border-r">
<SpaceRail />
</div>
<div class="min-h-0 min-w-0 overflow-hidden">
<slot />
</div>
</div>
</template>
20 changes: 20 additions & 0 deletions src/renderer/components/code-space-layout/CodeSpaceLayout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { useApp } from '@/composables'

const { isSidebarHidden } = useApp()
</script>

<template>
<div
class="grid h-screen"
:class="[
isSidebarHidden
? 'grid-cols-1'
: 'grid-cols-[var(--sidebar-width)_var(--snippet-list-width)_1fr]',
]"
>
<Sidebar v-show="!isSidebarHidden" />
<SnippetList v-show="!isSidebarHidden" />
<Editor />
</div>
</template>
16 changes: 14 additions & 2 deletions src/renderer/components/layout/TwoColumn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ interface Props {
title: string
leftSize?: string
rightSize?: string
showBack?: boolean
topSpace?: number
}

interface Emits {
Expand All @@ -16,13 +18,21 @@ interface Emits {
const props = withDefaults(defineProps<Props>(), {
leftSize: '220px',
rightSize: '1fr',
showBack: true,
topSpace: 0,
})

const emit = defineEmits<Emits>()

const gridTemplateColumns = computed(() => {
return `${props.leftSize} 1px ${props.rightSize}`
})

const leftHeaderStyle = computed(() => {
return {
paddingTop: `calc(var(--title-bar-height) + ${props.topSpace}px)`,
}
})
</script>

<template>
Expand All @@ -32,12 +42,14 @@ const gridTemplateColumns = computed(() => {
>
<div class="grid h-full min-h-0 grid-rows-[auto_1fr] overflow-hidden">
<div
class="mt-2 flex items-center justify-between gap-2 overflow-hidden px-2 pt-[var(--title-bar-height)] pb-2"
class="flex items-center justify-between gap-2 overflow-hidden px-2 pb-2"
:style="leftHeaderStyle"
>
<div class="truncate font-bold">
{{ title }}
</div>
<UiActionButton
v-if="showBack"
:tooltip="i18n.t('button.back')"
class="shrink-0"
@click="() => emit('back')"
Expand All @@ -50,7 +62,7 @@ const gridTemplateColumns = computed(() => {
</div>
</div>
<div class="bg-border" />
<div class="mt-2 h-full min-h-0 overflow-auto pt-[var(--title-bar-height)]">
<div class="h-full min-h-0 overflow-auto pt-[var(--title-bar-height)]">
<slot name="right" />
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/math-notebook/ResultsPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ function openDocumentation() {
</transition>

<div
class="border-border/40 mb-2 flex h-[28px] shrink-0 items-center justify-between border-t px-2"
class="border-border/40 mb-1 flex h-[28px] shrink-0 items-center justify-between border-t px-2"
>
<UiActionButton
type="iconText"
Expand Down
103 changes: 103 additions & 0 deletions src/renderer/components/space-rail/SpaceRail.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<script setup lang="ts">
import * as Tooltip from '@/components/ui/shadcn/tooltip'
import { useApp, useTheme } from '@/composables'
import { i18n, ipc } from '@/electron'
import { RouterName } from '@/router'
import { getSpaceDefinitions } from '@/spaceDefinitions'
import { Settings } from 'lucide-vue-next'
import { RouterLink, useRoute } from 'vue-router'
import packageJson from '../../../../package.json'

const { isSponsored } = useApp()
const { isDark } = useTheme()
const route = useRoute()

function openDonatePage() {
void ipc.invoke('system:open-external', 'https://masscode.io/donate/')
}

const spaces = computed(() => {
return getSpaceDefinitions().map(space => ({
...space,
active: space.isActive(route.name),
}))
})
</script>

<template>
<nav
class="flex h-full flex-col items-center px-2 pt-[calc(var(--title-bar-height)+8px)] pb-3"
:aria-label="i18n.t('spaces.label')"
>
<Tooltip.TooltipProvider>
<div class="flex w-full flex-col gap-1">
<RouterLink
v-for="space in spaces"
:key="space.id"
v-slot="{ navigate }"
custom
:to="space.to"
>
<Tooltip.Tooltip>
<Tooltip.TooltipTrigger as-child>
<button
type="button"
class="text-text-muted hover:bg-list-selection flex w-full cursor-default flex-col items-center gap-1 rounded-lg px-2 py-2 transition-colors"
:class="{
'bg-list-selection text-list-selection-fg': space.active,
}"
@click="navigate"
>
<component
:is="space.icon"
class="h-4 w-4 shrink-0"
/>
<span class="text-[10px] leading-none font-medium select-none">
{{ space.label }}
</span>
</button>
</Tooltip.TooltipTrigger>
<Tooltip.TooltipContent side="right">
{{ space.tooltip }}
</Tooltip.TooltipContent>
</Tooltip.Tooltip>
</RouterLink>
</div>
</Tooltip.TooltipProvider>
<div
v-if="!isSponsored"
class="mt-auto flex flex-1 flex-col items-center justify-end gap-2 pb-2"
>
<span
class="cursor-pointer text-center text-[9px] leading-none font-semibold tracking-[0.14em] uppercase select-none [writing-mode:sideways-lr]"
:class="isDark ? 'text-amber-300/70' : 'text-violet-500/70'"
role="link"
tabindex="0"
@click="openDonatePage"
@keydown.enter="openDonatePage"
@keydown.space.prevent="openDonatePage"
>
{{ i18n.t("messages:special.unsponsored") }}
</span>
<RouterLink
v-slot="{ navigate }"
custom
:to="{ name: RouterName.preferencesStorage }"
>
<button
type="button"
class="text-text-muted hover:bg-list-selection hover:text-list-selection-fg flex h-8 w-8 cursor-default items-center justify-center rounded-lg transition-colors"
:title="i18n.t('preferences:label')"
@click="navigate"
>
<Settings class="h-4 w-4" />
</button>
</RouterLink>
<div
class="text-text-muted/55 text-[10px] leading-none font-medium select-none"
>
v{{ packageJson.version }}
</div>
</div>
</nav>
</template>
62 changes: 62 additions & 0 deletions src/renderer/spaceDefinitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { Component } from 'vue'
import type { RouteLocationRaw, RouteRecordName } from 'vue-router'
import { i18n } from '@/electron'
import { RouterName } from '@/router'
import { Blocks, Calculator, Code2 } from 'lucide-vue-next'

export type SpaceId = 'code' | 'tools' | 'math'

export interface SpaceDefinition {
id: SpaceId
label: string
tooltip: string
icon: Component
to: RouteLocationRaw
isActive: (routeName: RouteRecordName | null | undefined) => boolean
}

function isRouteNameInSpace(
routeName: RouteRecordName | null | undefined,
prefix: string,
) {
return (
typeof routeName === 'string'
&& (routeName === prefix || routeName.startsWith(`${prefix}/`))
)
}

export function getSpaceDefinitions(): SpaceDefinition[] {
return [
{
id: 'code',
label: i18n.t('spaces.code'),
tooltip: i18n.t('spaces.codeTooltip'),
icon: Code2,
to: { name: RouterName.main },
isActive: routeName => routeName === RouterName.main,
},
{
id: 'tools',
label: i18n.t('spaces.tools'),
tooltip: i18n.t('spaces.toolsTooltip'),
icon: Blocks,
to: { name: RouterName.devtoolsCaseConverter },
isActive: routeName =>
isRouteNameInSpace(routeName, RouterName.devtools),
},
{
id: 'math',
label: i18n.t('spaces.math'),
tooltip: i18n.t('spaces.mathTooltip'),
icon: Calculator,
to: { name: RouterName.mathNotebook },
isActive: routeName => routeName === RouterName.mathNotebook,
},
]
}

export function isSpaceRouteName(
routeName: RouteRecordName | null | undefined,
) {
return getSpaceDefinitions().some(space => space.isActive(routeName))
}
2 changes: 1 addition & 1 deletion src/renderer/styles.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@import "tailwindcss";

:root {
--title-bar-height: 20px;
--title-bar-height: 10px;
--editor-tool-header-height: 28px;
}

Expand Down
4 changes: 2 additions & 2 deletions src/renderer/views/Devtools.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { i18n } from '@/electron'
import { router, RouterName } from '@/router'
import { RouterName } from '@/router'
import { useRoute } from 'vue-router'

const route = useRoute()
Expand Down Expand Up @@ -93,7 +93,7 @@ const generatorsNav = [
<template>
<LayoutTwoColumn
:title="i18n.t('devtools:label')"
@back="() => router.push({ name: RouterName.main })"
:show-back="false"
>
<template #left>
<div class="scrollbar h-full min-h-0 overflow-y-auto px-2">
Expand Down
19 changes: 1 addition & 18 deletions src/renderer/views/Main.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
<script setup lang="ts">
import { useApp } from '@/composables'

const { isSidebarHidden } = useApp()
</script>

<template>
<div
class="grid h-screen"
:class="[
isSidebarHidden
? 'grid-cols-1'
: 'grid-cols-[var(--sidebar-width)_var(--snippet-list-width)_1fr]',
]"
>
<Sidebar v-show="!isSidebarHidden" />
<SnippetList v-show="!isSidebarHidden" />
<Editor />
</div>
<CodeSpaceLayout />
</template>
Loading