fix(ui): format shortcut tooltips by platform
This commit is contained in:
parent
8bd9abac15
commit
883601f548
|
|
@ -478,6 +478,26 @@ const noPropertyPanelInternalsOutsidePanel = createImportRule(
|
|||
}
|
||||
)
|
||||
|
||||
const MACOS_MODIFIER_GLYPH_PATTERN = /[⌘⌥⌃]/u
|
||||
|
||||
const noHardcodedMacOSShortcutGlyphs = createTextRule(
|
||||
'open-pencil/no-hardcoded-macos-shortcut-glyphs',
|
||||
(sourceRel, content) => {
|
||||
if (!sourceRel.endsWith('.vue')) return []
|
||||
const diagnostics: Array<{ message: string; line?: number; column?: number }> = []
|
||||
for (const match of content.matchAll(MACOS_MODIFIER_GLYPH_PATTERN)) {
|
||||
const before = content.slice(0, match.index)
|
||||
const lines = before.split('\n')
|
||||
diagnostics.push({
|
||||
message: 'Use formatShortcut() instead of hardcoding macOS-only modifier glyphs.',
|
||||
line: lines.length,
|
||||
column: lines.at(-1)?.length ?? 0
|
||||
})
|
||||
}
|
||||
return diagnostics
|
||||
}
|
||||
)
|
||||
|
||||
const SHORTCUT_LABEL_PATTERN = /(?:Shift|Ctrl|Alt|Option|Cmd|Command|⌘|⇧|⌥|⌃)\s*[+)\w]/u
|
||||
|
||||
const noShortcutTextInLabels = createTextRule(
|
||||
|
|
@ -546,6 +566,7 @@ export const openPencilArchitecturePlugin = {
|
|||
noAppImportsInSharedUi,
|
||||
noPropertyPanelInternalsOutsidePanel,
|
||||
noShortcutTextInLabels,
|
||||
noHardcodedMacOSShortcutGlyphs,
|
||||
noUiImportsInCore
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
} from 'reka-ui'
|
||||
import { nextTick, ref, watch } from 'vue'
|
||||
|
||||
import { useEditorCommands, useI18n } from '@open-pencil/vue'
|
||||
import { useEditorCommands, useI18n, formatShortcut } from '@open-pencil/vue'
|
||||
import { menuItem, useMenuUI } from '@/components/ui/menu'
|
||||
import { useEditorStore } from '@/app/editor/active-store'
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ function zoomOut() {
|
|||
|
||||
const ZOOM_PRESETS: ReadonlyArray<{ label: string; level: number; shortcut?: string }> = [
|
||||
{ label: '50%', level: 0.5 },
|
||||
{ label: '100%', level: 1, shortcut: '⌘0' },
|
||||
{ label: '100%', level: 1, shortcut: 'MOD+0' },
|
||||
{ label: '200%', level: 2 }
|
||||
]
|
||||
|
||||
|
|
@ -126,15 +126,15 @@ watch(open, (v) => {
|
|||
|
||||
<DropdownMenuItem :class="itemCls" @select="zoomIn">
|
||||
<span class="flex-1">{{ menuText.zoomIn }}</span>
|
||||
<span class="text-[11px] text-muted">⌘+</span>
|
||||
<span class="text-[11px] text-muted">{{ formatShortcut('MOD++') }}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem :class="itemCls" @select="zoomOut">
|
||||
<span class="flex-1">{{ menuText.zoomOut }}</span>
|
||||
<span class="text-[11px] text-muted">⌘−</span>
|
||||
<span class="text-[11px] text-muted">{{ formatShortcut('MOD+−') }}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem :class="itemCls" @select="getCommand('view.zoomFit').run()">
|
||||
<span class="flex-1">{{ commands.zoomToFit }}</span>
|
||||
<span class="text-[11px] text-muted">⇧1</span>
|
||||
<span class="text-[11px] text-muted">{{ formatShortcut('SHIFT+1') }}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-for="preset in ZOOM_PRESETS"
|
||||
|
|
@ -144,7 +144,7 @@ watch(open, (v) => {
|
|||
>
|
||||
<icon-lucide-check v-if="isActivePreset(preset.level)" class="absolute left-2 size-3.5" />
|
||||
<span class="flex-1">{{ preset.label }}</span>
|
||||
<span v-if="preset.shortcut" class="text-[11px] text-muted">{{ preset.shortcut }}</span>
|
||||
<span v-if="preset.shortcut" class="text-[11px] text-muted">{{ formatShortcut(preset.shortcut) }}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator :class="menuCls.separator" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ToggleGroupItem, ToggleGroupRoot } from 'reka-ui'
|
||||
|
||||
import { TypographyControlsRoot, useI18n } from '@open-pencil/vue'
|
||||
import { TypographyControlsRoot, useI18n, formatShortcut } from '@open-pencil/vue'
|
||||
|
||||
import FontPicker from '@/components/FontPicker.vue'
|
||||
import FontSettingsPopover from '@/components/FontSettings/FontSettingsPopover.vue'
|
||||
|
|
@ -11,7 +11,7 @@ import Tip from '@/components/ui/Tip.vue'
|
|||
import { useSectionUI } from '@/components/ui/section'
|
||||
import { loadFont } from '@/app/editor/fonts'
|
||||
|
||||
const { panels } = useI18n()
|
||||
const { panels, menu } = useI18n()
|
||||
const sectionCls = useSectionUI()
|
||||
const fontLoader = { load: loadFont }
|
||||
</script>
|
||||
|
|
@ -125,7 +125,7 @@ const fontLoader = { load: loadFont }
|
|||
</ToggleGroupItem>
|
||||
</ToggleGroupRoot>
|
||||
<div class="flex gap-0.5">
|
||||
<Tip label="Bold (⌘B)">
|
||||
<Tip :label="`${menu.bold} (${formatShortcut('MOD+B')})`">
|
||||
<button
|
||||
data-test-id="typography-bold-button"
|
||||
class="flex cursor-pointer items-center justify-center rounded border border-border bg-input px-2 py-1 font-bold text-muted hover:bg-hover hover:text-surface data-[state=on]:border-accent data-[state=on]:bg-accent data-[state=on]:text-white"
|
||||
|
|
@ -135,7 +135,7 @@ const fontLoader = { load: loadFont }
|
|||
<icon-lucide-bold class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Italic (⌘I)">
|
||||
<Tip :label="`${menu.italic} (${formatShortcut('MOD+I')})`">
|
||||
<button
|
||||
class="flex cursor-pointer items-center justify-center rounded border border-border bg-input px-2 py-1 text-muted hover:bg-hover hover:text-surface data-[state=on]:border-accent data-[state=on]:bg-accent data-[state=on]:text-white"
|
||||
:data-state="ctx.activeFormatting.value.includes('italic') ? 'on' : 'off'"
|
||||
|
|
@ -144,7 +144,7 @@ const fontLoader = { load: loadFont }
|
|||
<icon-lucide-italic class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Underline (⌘U)">
|
||||
<Tip :label="`${menu.underline} (${formatShortcut('MOD+U')})`">
|
||||
<button
|
||||
class="flex cursor-pointer items-center justify-center rounded border border-border bg-input px-2 py-1 text-muted hover:bg-hover hover:text-surface data-[state=on]:border-accent data-[state=on]:bg-accent data-[state=on]:text-white"
|
||||
:data-state="ctx.activeFormatting.value.includes('underline') ? 'on' : 'off'"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useRoute } from 'vue-router'
|
|||
import { useHead } from '@unhead/vue'
|
||||
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from 'reka-ui'
|
||||
|
||||
import { useViewportKind } from '@open-pencil/vue'
|
||||
import { useViewportKind, formatShortcut } from '@open-pencil/vue'
|
||||
import { useKeyboard } from '@/app/shell/keyboard/use'
|
||||
import { loadEditorLayout, saveEditorLayout } from '@/app/shell/layout-storage'
|
||||
import { openFileFromPath, useMenu } from '@/app/shell/menu/use'
|
||||
|
|
@ -194,7 +194,7 @@ onUnmounted(() => {
|
|||
<button
|
||||
data-test-id="editor-show-ui"
|
||||
class="ml-1 flex size-6 cursor-pointer items-center justify-center rounded text-muted transition-colors hover:bg-hover hover:text-surface"
|
||||
title="Show UI (⌘\)"
|
||||
:title="`Show UI (${formatShortcut('MOD+\\')})`"
|
||||
@click="store.state.showUI = true"
|
||||
>
|
||||
<icon-lucide-sidebar class="size-3.5" />
|
||||
|
|
|
|||
Loading…
Reference in a new issue