The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup --gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
199 lines
5.1 KiB
Vue
199 lines
5.1 KiB
Vue
<template>
|
|
<div>
|
|
<!-- Image overlay toggle with M key -->
|
|
<transition name="fade">
|
|
<div
|
|
v-if="showImageOverlay"
|
|
class="fixed bottom-4 right-1 cursor-pointer pointer-events-auto w-auto z-200"
|
|
@click="toggleImageOverlay"
|
|
>
|
|
<div class="relative">
|
|
<img
|
|
src="/jpl-image.svg"
|
|
alt="JPL Image"
|
|
class="p-1 bg-gray-400 w-80 h-auto shadow-lg rounded-lg hover:shadow-xl transition-shadow"
|
|
/>
|
|
<div
|
|
class="absolute -bottom-8 left-1/2 transform -translate-x-1/2 text-xs text-gray-400 whitespace-nowrap pointer-events-none"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
</transition>
|
|
|
|
<div
|
|
class="slide-footer abs-br m-0 px-4 pt-2 pb-0 flex gap-3 items-center"
|
|
>
|
|
<!-- Iconos sociales -->
|
|
<a
|
|
href="https://jesusperez.pro"
|
|
target="_blank"
|
|
alt="Website"
|
|
class="text-lg slidev-icon-btn opacity-50 !border-none !hover:text-white"
|
|
>
|
|
<carbon:earth-europe-africa />
|
|
</a>
|
|
<a
|
|
href="https://github.com/jesusperezlorenzo"
|
|
target="_blank"
|
|
alt="GitHub"
|
|
class="text-lg slidev-icon-btn opacity-50 !border-none !hover:text-white"
|
|
>
|
|
<carbon-logo-github />
|
|
</a>
|
|
|
|
<!-- Botón Projection Mode - SIN fondo -->
|
|
<button
|
|
@click="toggleProjectionMode"
|
|
class="projection-btn"
|
|
:class="{ 'projection-active': isProjectionMode }"
|
|
:title="
|
|
isProjectionMode
|
|
? 'Modo Proyección ON (P/D)'
|
|
: 'Modo Proyección OFF (P/D)'
|
|
"
|
|
>
|
|
<span class="text-base">{{
|
|
isProjectionMode ? "📽️" : "🖥️"
|
|
}}</span>
|
|
</button>
|
|
|
|
<!-- Texto autor y versión -->
|
|
<span class="footer-text">Jesús Pérez · 2025 v1.1</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted, onUnmounted } from "vue";
|
|
|
|
const isProjectionMode = ref(false);
|
|
const showImageOverlay = ref(false);
|
|
|
|
const toggleProjectionMode = () => {
|
|
isProjectionMode.value = !isProjectionMode.value;
|
|
|
|
if (isProjectionMode.value) {
|
|
document.documentElement.classList.add("projection-mode");
|
|
localStorage.setItem("rust-vibe-projection", "true");
|
|
} else {
|
|
document.documentElement.classList.remove("projection-mode");
|
|
localStorage.setItem("rust-vibe-projection", "false");
|
|
}
|
|
};
|
|
|
|
// Toggle image overlay
|
|
const toggleImageOverlay = () => {
|
|
showImageOverlay.value = !showImageOverlay.value;
|
|
};
|
|
|
|
// Atajos de teclado P, D, y M
|
|
const handleKeyPress = (event: KeyboardEvent) => {
|
|
const target = event.target as HTMLElement;
|
|
|
|
// Ignore if typing in input or textarea
|
|
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
|
return;
|
|
}
|
|
|
|
// Handle P and D keys for projection mode
|
|
if (
|
|
event.key === "p" ||
|
|
event.key === "P" ||
|
|
event.key === "d" ||
|
|
event.key === "D"
|
|
) {
|
|
event.preventDefault();
|
|
toggleProjectionMode();
|
|
}
|
|
|
|
// Handle M key for image overlay toggle
|
|
if (event.key === "m" || event.key === "M") {
|
|
event.preventDefault();
|
|
toggleImageOverlay();
|
|
}
|
|
};
|
|
|
|
// Sync with localStorage on mount
|
|
onMounted(() => {
|
|
const saved = localStorage.getItem("rust-vibe-projection");
|
|
if (saved === "true") {
|
|
isProjectionMode.value = true;
|
|
document.documentElement.classList.add("projection-mode");
|
|
}
|
|
|
|
// Añadir listener de teclado global
|
|
window.addEventListener("keydown", handleKeyPress);
|
|
});
|
|
|
|
// Cleanup on unmount
|
|
onUnmounted(() => {
|
|
window.removeEventListener("keydown", handleKeyPress);
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Image overlay fade transition */
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.fade-enter-from,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
.fade-enter-to,
|
|
.fade-leave-from {
|
|
opacity: 1;
|
|
}
|
|
|
|
.slide-footer {
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
z-index: 10;
|
|
}
|
|
|
|
.slide-footer:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.footer-text {
|
|
font-size: 0.65rem;
|
|
/* Más pequeño que text-xs */
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.projection-btn {
|
|
opacity: 0.5;
|
|
transition: all 0.3s ease;
|
|
background: none !important;
|
|
/* SIN fondo, usa el de la slide */
|
|
border: none !important;
|
|
padding: 0.25rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.projection-btn:hover {
|
|
opacity: 1;
|
|
transform: scale(1.15);
|
|
}
|
|
|
|
.projection-btn.projection-active {
|
|
opacity: 1;
|
|
/* Sin background para que use el de la slide */
|
|
}
|
|
|
|
/* Ocultar en modo presentador */
|
|
.slidev-presenter .slide-footer {
|
|
display: none;
|
|
}
|
|
|
|
/* Ocultar al imprimir */
|
|
@media print {
|
|
.slide-footer {
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|