非常好, 让我们将代码转换为 TypeScript, 并添加适当的类型定义。以下是主要文件的 TypeScript 版本:

  1. src/index.ts:
import { Elysia } from "elysia";
import { html } from "@elysiajs/html";
import { staticPlugin } from "@elysiajs/static";
import { layout } from "./views/layout";
import { forecast } from "./views/forecast";
import { themesList, themeEditor } from "./views/themes";
import { getWeatherForecast, WeatherData } from "./weather";
import { getThemes, getTheme, saveTheme, Theme } from "./themes";
 
const app = new Elysia()
  .use(html())
  .use(staticPlugin())
  .get("/", ({ html }) => html(layout(forecast())))
  .get("/forecast", async ({ query, html }) => {
    const city = query.city as string;
    const days = parseInt(query.days as string, 10);
    if (city && days) {
      const weatherData = await getWeatherForecast(city, days);
      return html(forecast(city, days, weatherData));
    }
    return html(forecast());
  })
  .get("/themes", async ({ html }) => {
    const themes = await getThemes();
    return html(layout(themesList(themes)));
  })
  .get("/themes/edit/:name?", async ({ params, html }) => {
    const theme = params.name ? await getTheme(params.name) : null;
    return html(layout(themeEditor(theme)));
  })
  .post("/themes/save", async ({ body, html }) => {
    const theme = body as Theme;
    await saveTheme(theme);
    const themes = await getThemes();
    return html(themesList(themes));
  })
  .get("/apply-theme/:name", async ({ params, html }) => {
    const theme = await getTheme(params.name as string);
    if (theme) {
      return html(layout(forecast(), theme.content));
    }
    return html(layout(forecast()));
  })
  .get("/uno.css", async () => {
    const { createGenerator } = await import('unocss');
    const { default: presetUno } = await import('@unocss/preset-uno');
    const { default: presetIcons } = await import('@unocss/preset-icons');
    
    const uno = createGenerator({
      presets: [
        presetUno(),
        presetIcons({
          scale: 1.2,
          warn: true,
        }),
      ],
    });
 
    const { css } = await uno.generate('');
    return new Response(css, {
      headers: {
        'content-type': 'text/css',
      },
    });
  })
  .listen(3000);
 
console.log(`Server is running at http://localhost:${app.server?.port}`);
  1. src/views/layout.ts:
export const layout = (content: string, customStyles: string = '') => `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Weather Forecast</title>
  <script src="https://unpkg.com/htmx.org@1.9.6"></script>
  <link href="/uno.css" rel="stylesheet">
  <link href="/public/styles.css" rel="stylesheet">
  <style>${customStyles}</style>
</head>
<body class="bg-gray-100 text-gray-800">
  <div class="container mx-auto p-4">
    <h1 class="text-3xl font-bold mb-4 flex items-center"><i class="i-carbon-cloud mr-2"></i>Weather Forecast</h1>
    <nav class="mb-4 flex">
      <a href="/" class="mr-4 flex items-center"><i class="i-carbon-home mr-1"></i>Home</a>
      <a href="/themes" class="mr-4 flex items-center"><i class="i-carbon-list mr-1"></i>Themes</a>
    </nav>
    ${content}
  </div>
</body>
</html>
`;
  1. src/views/forecast.ts:
import { WeatherData } from "../weather";
 
const getWeatherIcon = (condition: string): string => {
  switch (condition.toLowerCase()) {
    case 'sunny':
      return 'i-carbon-sun';
    case 'cloudy':
      return 'i-carbon-cloud';
    case 'rainy':
      return 'i-carbon-rain';
    default:
      return 'i-carbon-cloud';
  }
};
 
export const forecast = (city?: string, days?: number, weatherData?: WeatherData[]): string => `
  <div class="mb-4">
    <form hx-get="/forecast" hx-target="#forecast" class="mb-4">
      <input type="text" name="city" placeholder="Enter city" required class="p-2 border rounded mr-2">
      <input type="number" name="days" placeholder="Number of days" required min="1" max="7" class="p-2 border rounded mr-2">
      <button type="submit" class="p-2 bg-blue-500 text-white rounded flex items-center">
        <i class="i-carbon-search mr-1"></i>Get Forecast
      </button>
    </form>
    <div id="forecast">
      ${weatherData ? `
        <h2 class="text-2xl mb-2">Weather Forecast for ${city} (${days} days)</h2>
        <ul>
          ${weatherData.map(day => `
            <li class="mb-2 flex items-center">
              <i class="${getWeatherIcon(day.condition)} mr-2"></i>
              ${day.date}: ${day.temperature}°C, ${day.condition}
            </li>
          `).join('')}
        </ul>
      ` : ''}
    </div>
  </div>
`;
  1. src/views/themes.ts:
import { Theme } from "../themes";
 
export const themesList = (themes: Theme[]): string => `
  <div class="mb-4">
    <h2 class="text-2xl mb-2 flex items-center"><i class="i-carbon-list mr-2"></i>Themes</h2>
    <ul>
      ${themes.map(theme => `
        <li class="mb-2 flex items-center">
          <i class="i-carbon-document mr-2"></i>${theme.name}
          <button hx-get="/apply-theme/${theme.name}" hx-target="body" class="ml-2 p-1 bg-blue-500 text-white rounded flex items-center">
            <i class="i-carbon-checkmark mr-1"></i>Apply
          </button>
          <a href="/themes/edit/${theme.name}" class="ml-2 p-1 bg-green-500 text-white rounded flex items-center">
            <i class="i-carbon-edit mr-1"></i>Edit
          </a>
        </li>
      `).join('')}
    </ul>
    <a href="/themes/edit" class="mt-4 inline-block p-2 bg-purple-500 text-white rounded flex items-center">
      <i class="i-carbon-add mr-1"></i>Create New Theme
    </a>
  </div>
`;
 
export const themeEditor = (theme: Theme | null): string => `
  <div class="mb-4">
    <h2 class="text-2xl mb-2 flex items-center">
      <i class="i-carbon-edit mr-2"></i>${theme ? 'Edit Theme' : 'Create New Theme'}
    </h2>
    <form hx-post="/themes/save" hx-swap="outerHTML">
      <div class="mb-2">
        <label for="name" class="block flex items-center"><i class="i-carbon-text-font mr-1"></i>Theme Name:</label>
        <input type="text" id="name" name="name" value="${theme?.name || ''}" required class="w-full p-2 border rounded">
      </div>
      <div class="mb-2">
        <label for="content" class="block flex items-center"><i class="i-carbon-code mr-1"></i>Theme Content:</label>
        <textarea id="content" name="content" rows="10" required class="w-full p-2 border rounded">${theme?.content || ''}</textarea>
      </div>
      <button type="submit" class="p-2 bg-blue-500 text-white rounded flex items-center">
        <i class="i-carbon-save mr-1"></i>Save Theme
      </button>
    </form>
  </div>
`;
  1. src/weather.ts:
export interface WeatherData {
  date: string;
  temperature: number;
  condition: string;
}
 
export async function getWeatherForecast(city: string, days: number): Promise<WeatherData[]> {
  // 模拟API调用
  return new Promise((resolve) => {
    setTimeout(() => {
      const forecast: WeatherData[] = Array.from({ length: days }, (_, i) => ({
        date: new Date(Date.now() + i * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
        temperature: Math.round(Math.random() * 30),
        condition: ['Sunny', 'Cloudy', 'Rainy'][Math.floor(Math.random() * 3)],
      }));
      resolve(forecast);
    }, 500);
  });
}
  1. src/themes.ts:
export interface Theme {
  name: string;
  content: string;
}
 
let themes: Theme[] = [
  { name: 'Default', content: '' },
  { name: 'Dark', content: 'body { background-color: #333; color: #fff; }' },
];
 
export async function getThemes(): Promise<Theme[]> {
  return themes;
}
 
export async function getTheme(name: string): Promise<Theme | undefined> {
  return themes.find(theme => theme.name === name);
}
 
export async function saveTheme(theme: Theme): Promise<void> {
  const index = themes.findIndex(t => t.name === theme.name);
  if (index !== -1) {
    themes[index] = theme;
  } else {
    themes.push(theme);
  }
}
  1. 创建 uno.config.ts:
import { defineConfig, presetUno, presetIcons } from 'unocss'
 
export default defineConfig({
  presets: [
    presetUno(),
    presetIcons({
      scale: 1.2,
      warn: true,
    }),
  ],
  safelist: 'i-carbon-home i-carbon-list i-carbon-edit i-carbon-add i-carbon-checkmark i-carbon-close i-carbon-sun i-carbon-cloud i-carbon-rain'.split(' '),
})

这些 TypeScript 文件提供了类型安全和更好的代码组织。确保更新你的 tsconfig.json 文件以包含适当的编译器选项,例如:

{
  "compilerOptions": {
    "target": "ES 2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

最后, 更新你的 package.json 脚本以使用 TypeScript:

{
  "scripts": {
    "dev": "bun run --watch src/index.ts"
  }
}

这样, 你就可以使用 bun run dev 来启动 TypeScript 版本的应用了。这个版本提供了更好的类型安全性和代码提示, 有助于减少错误并提高开发效率。