-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconstants.ts
More file actions
208 lines (179 loc) · 6.46 KB
/
constants.ts
File metadata and controls
208 lines (179 loc) · 6.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
export const DEFAULT_LM_STUDIO_API_URL = 'http://localhost:1234/v1/chat/completions';
export const DEFAULT_OLLAMA_API_URL = 'http://localhost:11434/api/chat';
export const DEFAULT_OLLAMA_CONTEXT_SIZE = 32000;
export const DEFAULT_API_URL = DEFAULT_LM_STUDIO_API_URL; // Backward-compatible alias
export const DEFAULT_MODEL = 'local-model'; // Placeholder
// --- SYSTEM PROMPT (LOCAL LLM OPTIMIZED) ---
export const SYSTEM_PROMPT = `You are LocalShip, an expert React/Electron Engineer.
GOAL
Build a high-quality, offline-ready App.
- Stack: React 18, Vite, Tailwind CSS v3, Electron.
- NO external CDNs (fonts/scripts must be local or omitted).
OUTPUT FORMAT RULES
1. You must output VALID MARKER BLOCKS for all code changes.
2. Do not include diffs or explanations outside the blocks if possible.
3. Never output <tool_call>, <toolcall>, <tool>, <function_call>, or any tool syntax.
4. Do not use <details>, <summary>, or any HTML wrappers.
5. Do not use <patch: ...> or <function=...> wrappers. Use ONLY:
<!-- filename: path/to/file.ext --> OR <!-- patch: path/to/file.ext -->
6. Single response mode: no prose outside marker blocks, no duplicate repeated patch blocks.
MODE A: CREATE NEW FILE
Use this for files not yet in [PROJECT MAP].
Filename blocks require fenced code.
<!-- filename: path/to/file.ext -->
\`\`\`ext
...full content...
\`\`\`
MODE B: PATCH EXISTING FILE
Use this for files found in [PROJECT MAP].
Patch blocks use XML ops only (no fenced wrapper required).
<!-- patch: path/to/file.ext -->
<replace>
<find>
...exact code snippet to replace (3-10 lines)...
</find>
<with>
...new code...
</with>
</replace>
<insert_after>
<find>...unique anchor...</find>
<with>...content to insert...</with>
</insert_after>
<delete>
<find>...content to delete...</find>
</delete>
CRITICAL:
- The <find> block must match the existing file EXACTLY (ignoring indentation).
- Do not reinvent file paths. Use the ones provided.
- Do not create a new top-level src/ directory unless explicitly requested.
- External libraries are allowed only when using local project dependencies.
- Never use CDN scripts/styles at runtime.
- For 3D requests, prefer \`import * as THREE from 'three'\`.
- Make the app fit to the available space. Only implement scrolling when absolutely needed or asked.
- If you cannot produce a valid patch, output ONE valid <!-- filename: App.tsx --> fenced block as a safe fallback.
- For typical feature requests, modify ONLY App.tsx unless asked otherwise.
- Do not touch index.html, input.css, tailwind.config.js unless the request explicitly asks for it.
CANONICAL VALID OUTPUT EXAMPLES
<!-- filename: App.tsx -->
\`\`\`tsx
import React from 'react';
const App: React.FC = () => <div>Hello</div>;
export default App;
\`\`\`
<!-- patch: App.tsx -->
<replace>
<find>
const title = "Old";
</find>
<with>
const title = "New";
</with>
</replace>
`;
export const ELECTRON_MAIN_JS = `
const { app, BrowserWindow, ipcMain, shell, dialog } = require('electron');
const path = require('path');
const fs = require('fs');
const os = require('os');
const { spawn } = require('child_process');
const postcss = require('postcss');
const tailwindcss = require('tailwindcss');
const autoprefixer = require('autoprefixer');
let mainWindow;
function createWindow() {
const preloadPath = path.join(__dirname, 'preload.js');
mainWindow = new BrowserWindow({
width: 1400,
height: 900,
titleBarStyle: 'hiddenInset',
webPreferences: {
preload: fs.existsSync(preloadPath) ? preloadPath : undefined,
nodeIntegration: false,
contextIsolation: true,
sandbox: false
},
backgroundColor: '#0f172a'
});
mainWindow.webContents.on('did-fail-load', (_event, code, desc, url) => {
dialog.showErrorBox('Renderer Load Failed', code + ' ' + desc + '\\n' + url);
});
mainWindow.webContents.on('render-process-gone', (_event, details) => {
dialog.showErrorBox('Renderer Crashed', JSON.stringify(details));
});
const isDev = !app.isPackaged && process.env.NODE_ENV !== 'production';
if (isDev) {
mainWindow.loadURL('http://localhost:5173');
} else {
const distPath = path.join(__dirname, 'dist/index.html');
const rootPath = path.join(__dirname, 'index.html');
mainWindow.loadFile(fs.existsSync(distPath) ? distPath : rootPath);
}
}
app.whenReady().then(createWindow);
ipcMain.handle('compile-tailwind', async (event, { files, configCode, cssInput }) => {
try {
const content = Object.entries(files).map(([filename, code]) => ({
raw: code,
extension: filename.split('.').pop()
}));
let userConfig = { content: ["./**/*.{js,ts,jsx,tsx,html}"] };
try {
const configMatch = configCode.match(/module\\.exports\\s*=\\s*(\\{[\\s\\S]*?\\})\\s*;?/);
if (configMatch) {
const configBody = configMatch[1];
const badTokens = ['require', 'import', 'function', '=>', 'process', 'globalThis', 'constructor', '__proto__', '/*', '//'];
if (!badTokens.some(token => configBody.includes(token))) {
userConfig = Function('"use strict"; return (' + configBody + ')')();
}
}
} catch (e) {
console.warn("Tailwind config parsing failed, using default.");
}
userConfig.content = content;
const result = await postcss([
tailwindcss(userConfig),
autoprefixer
]).process(cssInput, { from: undefined });
return { css: result.css };
} catch (error) {
console.error("Tailwind Compile Error:", error);
return { error: error.message };
}
});
`;
export const PRELOAD_JS = `const { contextBridge } = require('electron');
contextBridge.exposeInMainWorld('localship', { isPackaged: true });
`;
export const PACKAGE_JSON = (appName: string, author: string) => JSON.stringify({
name: appName.toLowerCase().replace(/[^a-z0-9-_]/g, '-'),
version: "1.0.0",
private: true,
main: "main.js",
scripts: {
dev: "vite",
"build:web": "vite build",
start: "electron .",
dist: "npm run build:web && electron-builder"
},
author: author,
dependencies: {
react: "^18.2.0",
"react-dom": "^18.2.0",
three: "^0.179.1",
tailwindcss: "^3.4.17",
autoprefixer: "^10.4.19",
postcss: "^8.4.38"
},
devDependencies: {
electron: "^30.0.0",
"electron-builder": "^24.0.0",
vite: "^5.4.8",
"@vitejs/plugin-react": "^4.3.2",
typescript: "^5.5.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0"
}
}, null, 2);
export const README_MD = `# Generated App
Built with LocalShip & Tailwind v3.`;