Files
makelore/electron/opencode/project-template.ts

140 lines
5.0 KiB
TypeScript

import { dirname, join } from 'node:path';
import { mkdir, writeFile } from 'node:fs/promises';
import type { ProjectType } from '../../shared/project-config';
import vitePackageJson from './project-templates/vite/package.json';
import vitePackageLock from './project-templates/vite/package-lock.json';
const VITE_CONFIG = `import { defineConfig } from 'vite';
export default defineConfig({
base: './',
});
`;
const MINI_GAME_FILES: Record<string, string> = {
'index.html': `<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Makelore 小游戏</title>
<style>
* { box-sizing: border-box; }
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; background: #10233d; }
body { display: grid; place-items: center; font-family: system-ui, sans-serif; }
#game { width: 100%; height: 100%; touch-action: none; }
</style>
</head>
<body>
<canvas id="game" aria-label="Makelore 小游戏画布"></canvas>
<script type="module" src="/game.js"></script>
</body>
</html>
`,
'game.json': `${JSON.stringify({ deviceOrientation: 'portrait', showStatusBar: false }, null, 2)}\n`,
'game.js': `const canvas = document.querySelector('#game');
const context = canvas.getContext('2d');
function render() {
const ratio = window.devicePixelRatio || 1;
const width = window.innerWidth;
const height = window.innerHeight;
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
context.setTransform(ratio, 0, 0, ratio, 0, 0);
const gradient = context.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, '#10233d');
gradient.addColorStop(1, '#3a5578');
context.fillStyle = gradient;
context.fillRect(0, 0, width, height);
context.textAlign = 'center';
context.fillStyle = '#ffffff';
context.font = '700 28px system-ui, sans-serif';
context.fillText('Makelore 小游戏', width / 2, height / 2 - 10);
context.fillStyle = '#f6b45f';
context.font = '16px system-ui, sans-serif';
context.fillText('点击画布,开始创造', width / 2, height / 2 + 26);
}
canvas.addEventListener('pointerdown', (event) => {
context.beginPath();
context.fillStyle = '#f26a3d';
context.arc(event.clientX, event.clientY, 18, 0, Math.PI * 2);
context.fill();
});
window.addEventListener('resize', render);
render();
`,
};
const MINI_PROGRAM_FILES: Record<string, string> = {
'index.html': `<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Makelore 小程序</title>
</head>
<body>
<main id="app"></main>
<script type="module" src="/pages/index/index.js"></script>
</body>
</html>
`,
'app.json': `${JSON.stringify({ pages: ['pages/index/index'], window: { navigationBarTitleText: 'Makelore 小程序' } }, null, 2)}\n`,
'app.js': `export const app = {
globalData: {
platform: 'makelore',
},
};
`,
'pages/index/index.json': `${JSON.stringify({ navigationBarTitleText: '首页' }, null, 2)}\n`,
'pages/index/index.css': `:root {
color: #24364b;
background: #f4f7fb;
font-family: system-ui, sans-serif;
}
body { margin: 0; }
.page { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.card { width: min(420px, 100%); border: 1px solid #d8e1ec; border-radius: 20px; background: #fff; padding: 28px; text-align: center; box-shadow: 0 16px 48px rgb(36 54 75 / 10%); }
button { border: 0; border-radius: 12px; background: #3a5578; color: #fff; padding: 12px 18px; font: inherit; font-weight: 700; cursor: pointer; }
`,
'pages/index/index.js': `import { app } from '../../app.js';
import './index.css';
const root = document.querySelector('#app');
let count = 0;
root.innerHTML = \`<section class="page"><div class="card"><p>运行于 \${app.globalData.platform}</p><h1>Makelore 小程序</h1><p id="count">已点击 0 次</p><button type="button">点我试试</button></div></section>\`;
root.querySelector('button').addEventListener('click', () => {
count += 1;
root.querySelector('#count').textContent = \`已点击 \${count}\`;
});
`,
};
function serializeJson(value: unknown): string {
return `${JSON.stringify(value, null, 2)}\n`;
}
export async function createPublishableProjectTemplate(
projectPath: string,
projectType: Exclude<ProjectType, 'custom'>,
): Promise<void> {
const files = projectType === 'mini_game' ? MINI_GAME_FILES : MINI_PROGRAM_FILES;
const templateFiles: Record<string, string> = {
'package.json': serializeJson(vitePackageJson),
'package-lock.json': serializeJson(vitePackageLock),
'vite.config.js': VITE_CONFIG,
...files,
};
for (const [relativePath, content] of Object.entries(templateFiles)) {
const filePath = join(projectPath, relativePath);
await mkdir(dirname(filePath), { recursive: true });
await writeFile(filePath, content, { encoding: 'utf8', flag: 'wx' });
}
}