diff --git a/src/style.css b/src/style.css
index 966e431..45fdcf3 100644
--- a/src/style.css
+++ b/src/style.css
@@ -209,6 +209,13 @@ body, html { margin: 0; padding: 0; width: 100%; height: 100%; background-color:
font-size: 11px; font-weight: bold; padding: 3px 8px; border: 1px solid currentColor;
background: rgba(0, 0, 0, 0.5); letter-spacing: 1px; border-radius: 2px; display: inline-block;
}
+.weather-panel { text-align: center; }
+.weather-ascii {
+ font-family: inherit; white-space: pre; text-align: left; display: inline-block;
+ font-size: 11px; line-height: 1.15; color: var(--tui-color); text-shadow: 0 0 4px var(--tui-glow);
+ margin: 0 0 8px; border: none; background: transparent;
+}
+.weather-loading { font-size: 11px; color: var(--tui-dim); letter-spacing: 0.05em; padding: 8px 0; }
/* ๐บ ์ ํ๋ธ ๊ทธ๋ฆฌ๋ ๋ ์ด์์ */
#videos-container {
diff --git a/src/views/Main.vue b/src/views/Main.vue
index 1d5705b..01c34ad 100644
--- a/src/views/Main.vue
+++ b/src/views/Main.vue
@@ -75,6 +75,17 @@
[{{ currentServerSourceDisplay }}]
+
SESSION
USER{{ isLoggedIn ? currentUsername : 'anonymous' }}
@@ -271,9 +282,77 @@ const showSystemAlert = (message) => {
const todayUnique = ref(0);
const activeNow = ref(0);
let analyticsTimer = null;
+let weatherTimer = null;
const currentServerSource = ref('nas');
+const WEATHER_ICONS = {
+ clear: " \\ /\n .-.\n โ ( ) โ\n `-'\n / \\",
+ cloudy: " .--.\n .-( ).\n (___.__)__)",
+ overcast: " .--.\n .-( ).\n (___.__)__)",
+ fog: " _ - _ - _\n_ - _ - _ -\n _ - _ - _",
+ drizzle: " .-.\n ( ).\n (___(__)\n ' ' ' '",
+ rain: " .-.\n ( ).\n (___(__)\n ' ' ' ' '",
+ snow: " .-.\n ( ).\n (___(__)\n * * *",
+ thunder: " .-.\n ( ).\n (___(__)\n โก ' โก"
+};
+
+const WEATHER_LABELS = {
+ clear: '๋ง์',
+ cloudy: '๊ตฌ๋ฆ ์กฐ๊ธ',
+ overcast: 'ํ๋ฆผ',
+ fog: '์๊ฐ',
+ drizzle: '์ด์ฌ๋น',
+ rain: '๋น',
+ snow: '๋',
+ thunder: '๋์ฐ'
+};
+
+const getWeatherIconKey = (code) => {
+ if (code === 0) return 'clear';
+ if (code === 1 || code === 2) return 'cloudy';
+ if (code === 3) return 'overcast';
+ if (code === 45 || code === 48) return 'fog';
+ if ([51, 53, 55, 56, 57].includes(code)) return 'drizzle';
+ if ([61, 63, 65, 66, 67, 80, 81, 82].includes(code)) return 'rain';
+ if ([71, 73, 75, 77, 85, 86].includes(code)) return 'snow';
+ if ([95, 96, 99].includes(code)) return 'thunder';
+ return 'cloudy';
+};
+
+const weather = ref({ loading: true, error: false, temp: null, iconKey: 'clear' });
+const weatherIcon = computed(() => WEATHER_ICONS[weather.value.iconKey] || WEATHER_ICONS.cloudy);
+const weatherLabel = computed(() => WEATHER_LABELS[weather.value.iconKey] || '');
+
+const fetchWeatherData = async () => {
+ try {
+ const res = await axios.get('https://api.open-meteo.com/v1/forecast', {
+ params: {
+ latitude: 37.5665,
+ longitude: 126.9780,
+ current_weather: true,
+ timezone: 'Asia/Seoul'
+ }
+ });
+ const current = res.data && res.data.current_weather;
+ if (current) {
+ weather.value = {
+ loading: false,
+ error: false,
+ temp: Math.round(current.temperature),
+ iconKey: getWeatherIconKey(current.weathercode)
+ };
+ } else {
+ weather.value.loading = false;
+ weather.value.error = true;
+ }
+ } catch (err) {
+ console.error("๋ ์จ ๋ฐ์ดํฐ ๋ก๋ ์คํจ:", err);
+ weather.value.loading = false;
+ weather.value.error = true;
+ }
+};
+
const menuItems = [
{ id: 'main', name: 'HOME' },
{ id: 'youtube', name: 'YOUTUBE' },
@@ -499,6 +578,7 @@ onMounted(() => {
fetchVideos();
fetchAnalyticsData();
+ fetchWeatherData();
detectRealtimeServerSource();
@@ -507,6 +587,8 @@ onMounted(() => {
detectRealtimeServerSource();
}, 30000);
+ weatherTimer = setInterval(fetchWeatherData, 600000);
+
const globaljQuery = window.$;
if (globaljQuery && terminalRef.value) {
termInstance.value = globaljQuery(terminalRef.value).terminal({
@@ -595,5 +677,6 @@ onMounted(() => {
onUnmounted(() => {
if (analyticsTimer) clearInterval(analyticsTimer);
+ if (weatherTimer) clearInterval(weatherTimer);
});