Keep the file extension always visible but discreetly as part of the filename. Fix Escape in rename field.

This commit is contained in:
2026-05-03 18:22:03 +00:00
parent 2406ea87b0
commit 31fc02ddbf
3 changed files with 96 additions and 26 deletions
+4
View File
@@ -115,6 +115,8 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
if (searchHasText && (dir === 'left' || dir === 'right')) { if (searchHasText && (dir === 'left' || dir === 'right')) {
return // Let browser handle cursor movement return // Let browser handle cursor movement
} }
// Don't intercept arrows for non-search inputs (e.g. rename input)
if (input && !searchInput) return
arrow = dir arrow = dir
} }
if (arrow) { if (arrow) {
@@ -136,6 +138,8 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
else if (keyup && event.key === 'Escape') { else if (keyup && event.key === 'Escape') {
store.error = '' store.error = ''
store.clearToast() store.clearToast()
// Keep rename and other non-search inputs isolated from search behavior.
if (input && !searchInput) return
headerMain.value!.clearSearch(event) headerMain.value!.clearSearch(event)
store.focusBreadcrumb() store.focusBreadcrumb()
} else if (!input && keyup && event.key === 'Backspace') { } else if (!input && keyup && event.key === 'Backspace') {
+2 -1
View File
@@ -8,6 +8,7 @@
:editing="editing === doc ? {rename, exit} : null" :editing="editing === doc ? {rename, exit} : null"
:style="{ '--gallery-figure-height': rowHeightsByKey[doc.key] ?? '15em' }" :style="{ '--gallery-figure-height': rowHeightsByKey[doc.key] ?? '15em' }"
@menu="contextMenu($event, doc)" @menu="contextMenu($event, doc)"
@rename="editing = doc; store.cursor = doc.key"
:class="{ 'folder-start': showFolderBreadcrumb(index) }" :class="{ 'folder-start': showFolderBreadcrumb(index) }"
/> />
</template> </template>
@@ -310,7 +311,7 @@ let scrolltr: any = null
watchEffect(() => { watchEffect(() => {
if (store.cursor && store.cursor !== editing.value?.key) editing.value = null if (store.cursor && store.cursor !== editing.value?.key) editing.value = null
if (editing.value) store.cursor = editing.value.key if (editing.value) store.cursor = editing.value.key
if (store.cursor) { if (store.cursor && !editing.value) {
const a = document.querySelector( const a = document.querySelector(
`#file-${store.cursor}` `#file-${store.cursor}`
) as HTMLAnchorElement | null ) as HTMLAnchorElement | null
+88 -23
View File
@@ -11,17 +11,26 @@
<figure> <figure>
<slot></slot> <slot></slot>
<MediaPreview ref=m :doc="doc" tabindex=-1 quality="sz=512" class="figcontent" /> <MediaPreview ref=m :doc="doc" tabindex=-1 quality="sz=512" class="figcontent" />
<div v-if="!doc.dir && doc.ext" class="ext-badge">{{ doc.ext }}</div>
<div class="titlespacer"></div> <div class="titlespacer"></div>
<figcaption @click.prevent @contextmenu.prevent="$emit('menu', $event)"> <figcaption @click.prevent @contextmenu.prevent="$emit('menu', $event)">
<template v-if="editing"> <template v-if="editing">
<SelectBox :doc=doc @click="store.cursor = doc.key"/>
<div class="filename-row rename-row">
<div class="rename-wrap"> <div class="rename-wrap">
<FileRenameInput :doc=doc :rename=editing.rename :exit=editing.exit /> <FileRenameInput :doc=doc :rename=editing.rename :exit=editing.exit />
</div> </div>
</div>
<div class=namespacer></div>
</template> </template>
<template v-else> <template v-else>
<SelectBox :doc=doc @click="store.cursor = doc.key"/> <SelectBox :doc=doc @click="store.cursor = doc.key"/>
<span>{{ displayName }}<SparseIndicator :doc="doc" class="after-name" /></span> <div class="filename-row">
<span class="filename-group">
<span class="filename">{{ displayName }}<SparseIndicator :doc="doc" class="after-name" /></span>
<span v-if="doc.ext" class="file-ext">.{{ doc.ext }}</span>
</span>
<button class="rename-btn" @click="$emit('rename')" title="Rename"></button>
</div>
<div class=namespacer></div> <div class=namespacer></div>
</template> </template>
</figcaption> </figcaption>
@@ -45,7 +54,7 @@ import SparseIndicator from './SparseIndicator.vue'
const store = useMainStore() const store = useMainStore()
type EditingProp = { type EditingProp = {
rename: (name: string) => void rename: (doc: Doc, newName: string) => void
exit: () => void exit: () => void
} }
@@ -90,19 +99,74 @@ const onclick = (ev: Event) => {
.after-name { .after-name {
margin-left: 0.3em; margin-left: 0.3em;
} }
.ext-badge { .filename-row {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0;
flex: 0 1 auto;
min-width: 0;
position: relative;
overflow: visible;
max-width: calc(100% - 4.5em);
}
.filename-row::after {
content: '';
position: absolute; position: absolute;
bottom: 2.5em; left: 100%;
right: 1em; top: 0;
width: 1.4em;
height: 100%;
}
.filename-group {
display: inline-flex;
align-items: baseline;
min-width: 0;
max-width: 100%;
}
.filename {
cursor: default;
padding: .5em 0;
color: #fff; color: #fff;
font-size: 0.65em; font-size: 0.8em;
font-weight: 600; font-weight: 600;
letter-spacing: 0.03em;
text-transform: uppercase;
text-shadow: 0 0 .2em #000, 0 0 .2em #000; text-shadow: 0 0 .2em #000, 0 0 .2em #000;
line-height: 1.4; text-wrap: nowrap;
text-overflow: ellipsis;
overflow: hidden;
flex: 0 1 auto;
min-width: 0;
}
.file-ext {
color: rgba(255, 255, 255, 0.8);
font-size: 0.8em;
font-weight: 600;
text-shadow: 0 0 .2em #000, 0 0 .2em #000;
padding: 0 .15em 0 0;
white-space: nowrap;
flex: 0 0 auto;
}
.rename-btn {
position: absolute;
left: 100%;
top: 50%;
transform: translate(0.2em, -50%);
z-index: 2;
background: none;
border: none;
padding: 0;
cursor: pointer;
font-size: 0.8em;
line-height: 1;
opacity: 0;
visibility: hidden;
pointer-events: none; pointer-events: none;
user-select: none; transition: opacity 0.12s ease;
}
.filename-row:hover .rename-btn {
opacity: 1;
visibility: visible;
pointer-events: auto;
} }
figure { figure {
height: var(--gallery-figure-height, 15em); height: var(--gallery-figure-height, 15em);
@@ -149,18 +213,10 @@ figcaption input[type='checkbox'] {
figcaption input[type='checkbox']:checked, figcaption:hover input[type='checkbox'] { figcaption input[type='checkbox']:checked, figcaption:hover input[type='checkbox'] {
opacity: 1; opacity: 1;
} }
figcaption span { .cursor .filename {
cursor: default; color: var(--accent-color);
padding: .5em;
color: #fff;
font-size: 0.8em;
font-weight: 600;
text-shadow: 0 0 .2em #000, 0 0 .2em #000;
text-wrap: nowrap;
text-overflow: ellipsis;
overflow: hidden;
} }
.cursor figcaption span { .cursor .file-ext {
color: var(--accent-color); color: var(--accent-color);
} }
figcaption .namespacer { figcaption .namespacer {
@@ -170,6 +226,15 @@ figcaption .namespacer {
} }
.rename-wrap { .rename-wrap {
font-size: 0.8em; font-size: 0.8em;
width: 100%; width: auto;
min-width: 0;
max-width: 100%;
}
.rename-row {
max-width: calc(100% - 4.5em);
}
.rename-wrap :deep(#FileRenameInput) {
min-width: 0;
max-width: 100%;
} }
</style> </style>