Skip to main content
Glama
App.vue5.09 kB
<script setup lang="ts"> import { ref, computed, watch, watchEffect, onMounted } from 'vue' import { useCalculatorStore } from '@/stores/calculator' import { useThemeProvider } from '@/composables/useTheme' import { useTimeFormatter } from '@/composables/useFormatter' import CalculatorInput from '@/components/CalculatorInput.vue' import CalculatorDisplay from '@/components/CalculatorDisplay.vue' // Get the calculator store const store = useCalculatorStore() // Use theme composable with provide/inject - provides theme to all child components const themeManager = useThemeProvider() // Use time formatter composable const timeFormatter = useTimeFormatter() // Local ref for app title const appTitle = ref('Vue Calculator') // Computed property for app version const appVersion = computed((): string => { return '1.0.0' }) // Computed property for greeting message const greetingMessage = computed((): string => { const hour = new Date().getHours() if (hour < 12) return 'Good morning' if (hour < 18) return 'Good afternoon' return 'Good evening' }) // Get statistics from store const totalCalculations = computed((): number => { return store.history.length }) // Check if calculator is active const isCalculatorActive = computed((): boolean => { return store.currentValue !== 0 || store.previousValue !== null }) // Get last calculation time const lastCalculationTime = computed((): string => { if (store.history.length === 0) return 'No calculations yet' const lastEntry = store.history[store.history.length - 1] return timeFormatter.getRelativeTime(lastEntry.timestamp) }) // Watch for calculation count changes - demonstrates watchEffect watchEffect(() => { document.title = `Calculator (${totalCalculations.value} calculations)` }) // Watch for theme changes - demonstrates watch watch( () => themeManager.isDarkMode.value, (isDark) => { console.log(`Theme changed to ${isDark ? 'dark' : 'light'} mode`) } ) // Lifecycle hook onMounted(() => { console.log('App component mounted') console.log(`Initial calculations: ${totalCalculations.value}`) }) // Toggle theme method const toggleTheme = () => { themeManager.toggleDarkMode() } <template> <div :class="['app', { 'dark-mode': themeManager.isDarkMode }]"> <header class="app-header"> <h1>{{ appTitle }}</h1> <div class="header-info"> <span class="greeting">{{ greetingMessage }}!</span> <span class="version">v{{ appVersion }}</span> <button @click="toggleTheme" class="btn-theme"> {{ themeManager.isDarkMode ? '☀️' : '🌙' }} </button> </div> </header> <main class="app-main"> <div class="calculator-container"> <div class="stats-bar"> <span>Total Calculations: {{ totalCalculations }}</span> <span :class="{ 'active-indicator': isCalculatorActive }"> {{ isCalculatorActive ? 'Active' : 'Idle' }} </span> <span class="last-calc-time">{{ lastCalculationTime }}</span> </div> <div class="calculator-grid"> <CalculatorInput /> <CalculatorDisplay /> </div> </div> </main> <footer class="app-footer"> <p>Built with Vue 3 + Pinia + TypeScript</p> </footer> </div> </template> <style scoped> .app { min-height: 100vh; display: flex; flex-direction: column; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); transition: background 0.3s ease; } .app.dark-mode { background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%); } .app-header { padding: 2rem; color: white; text-align: center; } .app-header h1 { margin: 0 0 1rem 0; font-size: 2.5rem; } .header-info { display: flex; justify-content: center; align-items: center; gap: 1rem; } .greeting { font-size: 1.1rem; } .version { font-size: 0.9rem; opacity: 0.8; } .btn-theme { background: rgba(255, 255, 255, 0.2); border: none; border-radius: 50%; width: 40px; height: 40px; font-size: 1.2rem; cursor: pointer; transition: background 0.2s; } .btn-theme:hover { background: rgba(255, 255, 255, 0.3); } .app-main { flex: 1; display: flex; justify-content: center; align-items: center; padding: 2rem; } .calculator-container { width: 100%; max-width: 1200px; display: flex; flex-direction: column; gap: 1rem; } .stats-bar { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 1rem; background: rgba(255, 255, 255, 0.9); border-radius: 8px; font-weight: 500; } .last-calc-time { font-size: 0.9rem; color: #666; font-style: italic; } .active-indicator { color: #4caf50; font-weight: bold; } .active-indicator:not(.active-indicator) { color: #999; } .calculator-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; } @media (max-width: 768px) { .calculator-grid { grid-template-columns: 1fr; } } .app-footer { padding: 1rem; text-align: center; color: white; opacity: 0.8; } .app-footer p { margin: 0; } </style>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ryota-murakami/serena'

If you have feedback or need assistance with the MCP directory API, please join our Discord server