Files
librenotes/internal/notesium/web/app/nav-tabs.js
T
libretechandClaude Opus 4.7 094250609c Fork Notesium source and restructure into Go package layout
Initial fork of github.com/alonswartz/notesium into librenotes:
- Source moved to internal/notesium/ (package notesium)
- Thin entry point at cmd/librenotes/main.go
- Module renamed to git.librete.ch/public/librenotes
- main() exposed as notesium.Run()
- LICENSE preserved (MIT), NOTICE added with attribution
- Web assets and completion.bash co-located with embedding code
  to satisfy go:embed path constraints

Closes #3, #34, #35.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 21:52:25 +02:00

123 lines
4.3 KiB
JavaScript

var t = `
<TransitionGroup name="nav-tabs" tag="div"
leave-active-class="transition-none"
enter-active-class="transition-transform duration-200 ease-in-out"
move-class="transition-transform duration-250 ease-in-out"
class="flex flex-nowrap max-w-full w-full h-9 overflow-x-hidden items-center px-2 mr-6">
<div v-for="(tab, index) in getTabs" :key="tab.id" class="flex items-center h-full flex-1 min-w-0 max-w-[13rem]">
<div :class="isActive(tab.id) ? 'text-gray-50' : 'text-transparent'" class="relative h-full">
<svg class="absolute right-0 bottom-0" fill="currentColor" width="7" height="7"><path d="M 0 7 A 7 7 0 0 0 7 0 L 7 7 Z"></path></svg>
</div>
<div
@click="$emit('tab-activate', tab.id)"
draggable="true"
@dragstart="onDragStart(index)"
@dragover.prevent="onDragOver(index)"
@drop="onDrop"
@dragend="onDragEnd"
:title="tab.titleHover"
:class="isActive(tab.id) ? 'bg-gray-50 text-gray-800' : 'hover:bg-gray-100/75 hover:text-gray-700 text-gray-500'"
class="flex items-center justify-between rounded-t-lg text-xs h-full pl-3 pr-2 cursor-pointer w-full truncate">
<div class="flex items-center gap-2 truncate min-w-0">
<span v-show="tab.isModified" class="inline-block h-2 w-2 rounded-full bg-yellow-400"></span>
<span class="truncate" v-text="tab.title"></span>
</div>
<span @click.stop="handleClose(tab.id, tab.type)" class="hover:bg-gray-300 hover:rounded-full">
<Icon name="mini-x-mark" size="h-4 w-4" />
</span>
</div>
<div :class="isActive(tab.id) ? 'text-gray-50' : 'text-transparent'" class="relative h-full">
<svg class="absolute bottom-0" fill="currentColor" width="7" height="7"><path d="M 0 0 A 7 7 0 0 0 7 7 L 0 7 Z"></path></svg>
</div>
<span :class="!isActive(tab.id) ? 'text-gray-300' : 'text-transparent'" class="z-1 -mr-1 -mt-[3px]">|</span>
</div>
</TransitionGroup>
`
import Icon from './icon.js'
export default {
components: { Icon },
props: ['tabs', 'activeTabId', 'previousTabId', 'notes'],
emits: ['tab-activate', 'tab-move', 'tab-close', 'note-close'],
data() {
return {
dragIndex: null,
dragOverEnabled: true,
}
},
methods: {
onDragStart(index) {
this.$emit('tab-activate', this.tabs[index].id)
this.dragIndex = index;
},
onDragOver(overIndex) {
if (!this.dragOverEnabled) return;
if (this.dragIndex === null || this.dragIndex === overIndex) return;
this.$emit('tab-move', this.tabs[this.dragIndex].id, overIndex);
this.dragIndex = overIndex;
this.dragOverEnabled = false;
setTimeout(() => { this.dragOverEnabled = true; }, 300);
},
onDrop() {
this.dragIndex = null;
},
onDragEnd() {
this.dragIndex = null;
},
isActive(tabId) {
return this.activeTabId == tabId;
},
handleClose(tabId, tabType) {
if (tabType === 'note') {
this.$emit('note-close', tabId);
return;
}
this.$emit('tab-close', tabId);
},
handleKeyPress(event) {
if (event.target.tagName !== 'BODY') return
if (event.ctrlKey && event.code == 'Digit6') {
this.previousTabId && this.$emit('tab-activate', this.previousTabId);
event.preventDefault();
return;
}
if (event.ctrlKey && (event.code == 'KeyH' || event.code == 'KeyL')) {
const index = this.tabs.findIndex(t => t.id === this.activeTabId);
if (index === -1) return;
const movement = event.code === 'KeyL' ? 1 : -1;
const newIndex = (index + movement + this.tabs.length) % this.tabs.length;
this.$emit('tab-activate', this.tabs[newIndex].id);
event.preventDefault();
return;
}
},
},
computed: {
getTabs() {
return this.tabs.map(tab => {
if (tab.type === 'note') {
const note = this.notes.find(n => n.Filename === tab.id);
return {
id: tab.id,
type: tab.type,
title: note.Title,
titleHover: `${note.Title} (${note.Filename})`,
isModified: note.isModified || false,
};
}
});
},
},
mounted() {
document.addEventListener('keydown', this.handleKeyPress);
},
beforeDestroy() {
document.removeEventListener('keydown', this.handleKeyPress);
},
template: t
}