anhedral 0.4.2 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -265
- package/assets/anhedral-cli-init-technical.svg +322 -0
- package/assets/anhedral-cli-init.svg +257 -173
- package/dist/bin.js +28 -191
- package/dist/dependencies.d.ts +7 -57
- package/dist/dependencies.js +96 -308
- package/dist/index.d.ts +2 -7
- package/dist/index.js +1 -4
- package/dist/platforms/desktop.js +401 -0
- package/dist/platforms/extension.js +401 -0
- package/dist/platforms/mobile.js +232 -0
- package/dist/project.d.ts +0 -26
- package/dist/standard-products.d.ts +2 -0
- package/dist/standard-products.js +6 -0
- package/dist/standard.d.ts +18 -0
- package/dist/standard.js +595 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/application-stack-standard.md +700 -0
- package/package.json +29 -51
- package/CHANGELOG.md +0 -178
- package/CONTRIBUTING.md +0 -77
- package/SECURITY.md +0 -37
- package/SKILL.md +0 -339
- package/assets/images/svg/ably-symbol-color.svg +0 -8
- package/bin/anhedral.js +0 -2
- package/dist/architecture/contributions.d.ts +0 -28
- package/dist/architecture/contributions.js +0 -125
- package/dist/architecture/index.d.ts +0 -12
- package/dist/architecture/index.js +0 -6
- package/dist/architecture/manifest.d.ts +0 -53
- package/dist/architecture/manifest.js +0 -283
- package/dist/architecture/modules.d.ts +0 -41
- package/dist/architecture/modules.js +0 -187
- package/dist/architecture/plan.d.ts +0 -58
- package/dist/architecture/plan.js +0 -145
- package/dist/architecture/templates.d.ts +0 -10
- package/dist/architecture/templates.js +0 -23
- package/dist/cli.d.ts +0 -32
- package/dist/cli.js +0 -281
- package/dist/composers/root.d.ts +0 -6
- package/dist/composers/root.js +0 -35
- package/dist/print.d.ts +0 -8
- package/dist/print.js +0 -48
- package/dist/prompts.d.ts +0 -6
- package/dist/prompts.js +0 -41
- package/dist/scaffold.d.ts +0 -64
- package/dist/scaffold.js +0 -2102
- package/dist/template-source.d.ts +0 -20
- package/dist/template-source.js +0 -180
- package/dist/templates/api.d.ts +0 -2
- package/dist/templates/api.js +0 -3187
- package/dist/templates/assets-private-proxy.d.ts +0 -3
- package/dist/templates/assets-private-proxy.js +0 -277
- package/dist/templates/desktop.js +0 -792
- package/dist/templates/electron-updater.d.ts +0 -3
- package/dist/templates/electron-updater.js +0 -358
- package/dist/templates/extension.js +0 -856
- package/dist/templates/first-run.d.ts +0 -2
- package/dist/templates/first-run.js +0 -167
- package/dist/templates/mobile.js +0 -861
- package/dist/templates/shared.d.ts +0 -2
- package/dist/templates/shared.js +0 -577
- package/dist/templates/web.d.ts +0 -2
- package/dist/templates/web.js +0 -569
- package/dist/toolchain.d.ts +0 -3
- package/dist/toolchain.js +0 -8
- package/dist/ui.d.ts +0 -33
- package/dist/ui.js +0 -199
- package/dist/workspace-config.d.ts +0 -11
- package/dist/workspace-config.js +0 -372
- package/docs/NORTHSTAR.md +0 -114
- package/docs/conventions.md +0 -94
- package/docs/engineering-principles.md +0 -88
- package/docs/master-stack-map.md +0 -199
- package/docs/output-tree-contract.md +0 -493
- package/docs/references/manual-scaffolding.md +0 -421
- package/docs/references/provisioning.md +0 -577
- package/scripts/render-master-stack-map.mjs +0 -510
- package/templates/api-fastify/apps/api/tsconfig.json +0 -20
- package/templates/catalog.json +0 -23
- package/templates/db-drizzle/packages/db/tsconfig.json +0 -14
- package/templates/desktop-electron/apps/desktop/tsconfig.json +0 -37
- package/templates/extension-wxt/apps/extension/tsconfig.json +0 -41
- package/templates/mobile-expo/apps/mobile/expo-env.d.ts +0 -1
- package/templates/web-next/apps/web/next-env.d.ts +0 -6
- /package/dist/{templates → platforms}/desktop.d.ts +0 -0
- /package/dist/{templates → platforms}/extension.d.ts +0 -0
- /package/dist/{templates → platforms}/mobile.d.ts +0 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeFile } from '../util.js';
|
|
3
|
+
import { DESKTOP_DEPENDENCIES } from '../dependencies.js';
|
|
4
|
+
import { childPackageName, htmlText, identifierSegment, jsString } from '../render.js';
|
|
5
|
+
export async function scaffoldDesktop(root, options) {
|
|
6
|
+
const { projectName, displayName } = options;
|
|
7
|
+
const dir = path.join(root, 'apps/desktop');
|
|
8
|
+
writePackageJson(dir, projectName);
|
|
9
|
+
writeTsConfig(dir);
|
|
10
|
+
writeViteConfig(dir);
|
|
11
|
+
writePostcssConfig(dir);
|
|
12
|
+
writeDevScript(dir);
|
|
13
|
+
writeShadcnConfig(dir);
|
|
14
|
+
writeEnvExample(dir);
|
|
15
|
+
writeSourceFiles(dir, displayName);
|
|
16
|
+
}
|
|
17
|
+
function writePackageJson(dir, projectName) {
|
|
18
|
+
const updatePublish = {};
|
|
19
|
+
writeFile(path.join(dir, 'package.json'), JSON.stringify({
|
|
20
|
+
name: childPackageName(projectName, 'desktop'),
|
|
21
|
+
version: '0.1.0',
|
|
22
|
+
private: true,
|
|
23
|
+
type: 'module',
|
|
24
|
+
main: 'dist/main/main.js',
|
|
25
|
+
scripts: {
|
|
26
|
+
dev: 'tsc -p tsconfig.main.json && node scripts/dev.mjs',
|
|
27
|
+
build: 'tsc --noEmit && tsc -p tsconfig.main.json && vite build',
|
|
28
|
+
typecheck: 'tsc --noEmit',
|
|
29
|
+
'build:mac': `pnpm build && electron-builder --mac${''}`,
|
|
30
|
+
'build:win': `pnpm build && electron-builder --win${''}`,
|
|
31
|
+
'build:linux': `pnpm build && electron-builder --linux${''}`,
|
|
32
|
+
package: `pnpm build && electron-builder${''}`,
|
|
33
|
+
...({}),
|
|
34
|
+
},
|
|
35
|
+
build: {
|
|
36
|
+
appId: `dev.anhedral.${identifierSegment(projectName)}`,
|
|
37
|
+
productName: projectName,
|
|
38
|
+
directories: {
|
|
39
|
+
output: 'release',
|
|
40
|
+
},
|
|
41
|
+
files: [
|
|
42
|
+
'dist/**/*',
|
|
43
|
+
'package.json',
|
|
44
|
+
],
|
|
45
|
+
mac: {
|
|
46
|
+
target: ['dmg', 'zip'],
|
|
47
|
+
},
|
|
48
|
+
win: {
|
|
49
|
+
target: ['nsis', 'zip'],
|
|
50
|
+
},
|
|
51
|
+
linux: {
|
|
52
|
+
executableName: identifierSegment(projectName),
|
|
53
|
+
target: ['AppImage', 'deb'],
|
|
54
|
+
},
|
|
55
|
+
...updatePublish,
|
|
56
|
+
},
|
|
57
|
+
dependencies: DESKTOP_DEPENDENCIES.dependencies,
|
|
58
|
+
devDependencies: DESKTOP_DEPENDENCIES.devDependencies,
|
|
59
|
+
}, null, 2) + '\n');
|
|
60
|
+
}
|
|
61
|
+
function writeTsConfig(dir) {
|
|
62
|
+
writeFile(path.join(dir, 'tsconfig.main.json'), JSON.stringify({
|
|
63
|
+
compilerOptions: {
|
|
64
|
+
target: 'ES2022',
|
|
65
|
+
module: 'NodeNext',
|
|
66
|
+
moduleResolution: 'NodeNext',
|
|
67
|
+
strict: true,
|
|
68
|
+
skipLibCheck: true,
|
|
69
|
+
esModuleInterop: true,
|
|
70
|
+
outDir: 'dist/main',
|
|
71
|
+
rootDir: 'src/main',
|
|
72
|
+
types: ['node'],
|
|
73
|
+
},
|
|
74
|
+
include: ['src/main/**/*'],
|
|
75
|
+
}, null, 2) + '\n');
|
|
76
|
+
}
|
|
77
|
+
function writeViteConfig(dir) {
|
|
78
|
+
writeFile(path.join(dir, 'vite.config.ts'), `import { defineConfig } from 'vite';
|
|
79
|
+
import react from '@vitejs/plugin-react';
|
|
80
|
+
import path from 'node:path';
|
|
81
|
+
|
|
82
|
+
export default defineConfig({
|
|
83
|
+
base: './',
|
|
84
|
+
plugins: [react()],
|
|
85
|
+
resolve: {
|
|
86
|
+
alias: {
|
|
87
|
+
'@': path.resolve(__dirname, 'src/renderer'),
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
build: {
|
|
91
|
+
outDir: 'dist/renderer',
|
|
92
|
+
emptyOutDir: true,
|
|
93
|
+
// Clerk is already loaded behind a dynamic import. Its optional account-management
|
|
94
|
+
// surface is intentionally a large async chunk and does not inflate initial rendering.
|
|
95
|
+
chunkSizeWarningLimit: 1600,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
`);
|
|
99
|
+
}
|
|
100
|
+
function writePostcssConfig(dir) {
|
|
101
|
+
writeFile(path.join(dir, 'postcss.config.mjs'), `const config = {
|
|
102
|
+
plugins: {
|
|
103
|
+
'@tailwindcss/postcss': {},
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export default config;
|
|
108
|
+
`);
|
|
109
|
+
}
|
|
110
|
+
function writeDevScript(dir) {
|
|
111
|
+
writeFile(path.join(dir, 'scripts/dev.mjs'), `import { spawn } from 'node:child_process';
|
|
112
|
+
import { once } from 'node:events';
|
|
113
|
+
|
|
114
|
+
const host = '127.0.0.1';
|
|
115
|
+
const port = '5173';
|
|
116
|
+
const devServerUrl = 'http://' + host + ':' + port;
|
|
117
|
+
const viteCommand = process.platform === 'win32' ? 'vite.cmd' : 'vite';
|
|
118
|
+
const electronCommand = process.platform === 'win32' ? 'electron.cmd' : 'electron';
|
|
119
|
+
|
|
120
|
+
function start(command, args, options = {}) {
|
|
121
|
+
const invocation = process.platform === 'win32'
|
|
122
|
+
? {
|
|
123
|
+
command: process.env.ComSpec || 'cmd.exe',
|
|
124
|
+
args: ['/d', '/s', '/c', 'call', command, ...args],
|
|
125
|
+
}
|
|
126
|
+
: { command, args };
|
|
127
|
+
return spawn(invocation.command, invocation.args, {
|
|
128
|
+
cwd: process.cwd(),
|
|
129
|
+
stdio: 'inherit',
|
|
130
|
+
...options,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function stop(child) {
|
|
135
|
+
if (child && child.exitCode === null && !child.killed) child.kill('SIGTERM');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function waitForServer(server, url, timeoutMs = 30_000) {
|
|
139
|
+
const deadline = Date.now() + timeoutMs;
|
|
140
|
+
while (Date.now() < deadline) {
|
|
141
|
+
if (server.exitCode !== null) throw new Error('Vite exited before the dev server was ready.');
|
|
142
|
+
try {
|
|
143
|
+
const response = await fetch(url);
|
|
144
|
+
if (response.ok) return;
|
|
145
|
+
} catch {
|
|
146
|
+
// The server is still starting.
|
|
147
|
+
}
|
|
148
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
149
|
+
}
|
|
150
|
+
throw new Error('Timed out waiting for Vite at ' + url + '.');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const vite = start(viteCommand, ['--host', host, '--port', port, '--strictPort']);
|
|
154
|
+
let electron;
|
|
155
|
+
let shuttingDown = false;
|
|
156
|
+
|
|
157
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
158
|
+
process.once(signal, () => {
|
|
159
|
+
shuttingDown = true;
|
|
160
|
+
stop(electron);
|
|
161
|
+
stop(vite);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
await waitForServer(vite, devServerUrl);
|
|
167
|
+
electron = start(electronCommand, ['.'], {
|
|
168
|
+
env: { ...process.env, VITE_DEV_SERVER_URL: devServerUrl },
|
|
169
|
+
});
|
|
170
|
+
vite.once('exit', (code) => {
|
|
171
|
+
if (!shuttingDown) {
|
|
172
|
+
console.error('Vite exited while Electron was running.');
|
|
173
|
+
process.exitCode = code ?? 1;
|
|
174
|
+
stop(electron);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
const [code] = await once(electron, 'exit');
|
|
178
|
+
if (process.exitCode === undefined) process.exitCode = typeof code === 'number' ? code : 1;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error(error instanceof Error ? error.message : error);
|
|
181
|
+
process.exitCode = 1;
|
|
182
|
+
} finally {
|
|
183
|
+
shuttingDown = true;
|
|
184
|
+
stop(electron);
|
|
185
|
+
stop(vite);
|
|
186
|
+
}
|
|
187
|
+
`);
|
|
188
|
+
}
|
|
189
|
+
function writeShadcnConfig(dir) {
|
|
190
|
+
writeFile(path.join(dir, 'components.json'), JSON.stringify({
|
|
191
|
+
$schema: 'https://ui.shadcn.com/schema.json',
|
|
192
|
+
style: 'new-york',
|
|
193
|
+
rsc: false,
|
|
194
|
+
tsx: true,
|
|
195
|
+
tailwind: {
|
|
196
|
+
config: '',
|
|
197
|
+
css: 'src/renderer/styles.css',
|
|
198
|
+
baseColor: 'neutral',
|
|
199
|
+
cssVariables: true,
|
|
200
|
+
prefix: '',
|
|
201
|
+
},
|
|
202
|
+
aliases: {
|
|
203
|
+
components: '@/components',
|
|
204
|
+
utils: '@/lib/utils',
|
|
205
|
+
ui: '@/components/ui',
|
|
206
|
+
lib: '@/lib',
|
|
207
|
+
hooks: '@/hooks',
|
|
208
|
+
},
|
|
209
|
+
iconLibrary: 'lucide',
|
|
210
|
+
}, null, 2) + '\n');
|
|
211
|
+
}
|
|
212
|
+
function writeEnvExample(dir) {
|
|
213
|
+
writeFile(path.join(dir, '.env.example'), '');
|
|
214
|
+
}
|
|
215
|
+
function writeSourceFiles(dir, displayName) {
|
|
216
|
+
const displayNameLiteral = jsString(displayName);
|
|
217
|
+
const displayNameHtml = htmlText(displayName);
|
|
218
|
+
writeFile(path.join(dir, 'src/main/app-window.ts'), `import { BrowserWindow, shell } from 'electron';
|
|
219
|
+
import path from 'node:path';
|
|
220
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
221
|
+
|
|
222
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
223
|
+
const rendererIndex = path.join(__dirname, '../renderer/index.html');
|
|
224
|
+
|
|
225
|
+
function isSafeExternalUrl(value: string): boolean {
|
|
226
|
+
try {
|
|
227
|
+
const url = new URL(value);
|
|
228
|
+
return url.protocol === 'https:' && !url.username && !url.password;
|
|
229
|
+
} catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function isAllowedAppNavigation(value: string): boolean {
|
|
235
|
+
try {
|
|
236
|
+
const target = new URL(value);
|
|
237
|
+
if (process.env.VITE_DEV_SERVER_URL) {
|
|
238
|
+
return target.origin === new URL(process.env.VITE_DEV_SERVER_URL).origin;
|
|
239
|
+
}
|
|
240
|
+
return target.href === pathToFileURL(rendererIndex).href;
|
|
241
|
+
} catch {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Product-owned window options and navigation policy belong in this file.
|
|
247
|
+
// Anhedral's managed main.ts imports this seam and wires lifecycle integrations around it.
|
|
248
|
+
export function createAppWindow(): BrowserWindow {
|
|
249
|
+
const window = new BrowserWindow({
|
|
250
|
+
width: 1180,
|
|
251
|
+
height: 760,
|
|
252
|
+
minWidth: 860,
|
|
253
|
+
minHeight: 560,
|
|
254
|
+
title: ${displayNameLiteral},
|
|
255
|
+
webPreferences: {
|
|
256
|
+
preload: path.join(__dirname, 'preload.cjs'),
|
|
257
|
+
contextIsolation: true,
|
|
258
|
+
nodeIntegration: false,
|
|
259
|
+
sandbox: true,
|
|
260
|
+
webSecurity: true,
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
window.webContents.on('will-navigate', (event, url) => {
|
|
265
|
+
if (!isAllowedAppNavigation(url)) event.preventDefault();
|
|
266
|
+
});
|
|
267
|
+
window.webContents.setWindowOpenHandler(({ url }) => {
|
|
268
|
+
if (isSafeExternalUrl(url)) {
|
|
269
|
+
void shell.openExternal(url).catch((error) => {
|
|
270
|
+
console.error('Unable to open external URL:', error);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return { action: 'deny' };
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
if (process.env.VITE_DEV_SERVER_URL) {
|
|
277
|
+
void window.loadURL(process.env.VITE_DEV_SERVER_URL).catch((error) => {
|
|
278
|
+
console.error('Unable to load the development renderer:', error);
|
|
279
|
+
});
|
|
280
|
+
} else {
|
|
281
|
+
void window.loadFile(rendererIndex).catch((error) => {
|
|
282
|
+
console.error('Unable to load the packaged renderer:', error);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return window;
|
|
287
|
+
}
|
|
288
|
+
`);
|
|
289
|
+
writeFile(path.join(dir, 'src/main/main.ts'), `import { app, BrowserWindow, session } from 'electron';
|
|
290
|
+
import { createAppWindow } from './app-window.js';
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
app.whenReady().then(() => {
|
|
294
|
+
session.defaultSession.setPermissionCheckHandler(() => false);
|
|
295
|
+
session.defaultSession.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false));
|
|
296
|
+
createAppWindow();
|
|
297
|
+
});
|
|
298
|
+
app.on('window-all-closed', () => {
|
|
299
|
+
if (process.platform !== 'darwin') app.quit();
|
|
300
|
+
});
|
|
301
|
+
app.on('activate', () => {
|
|
302
|
+
if (BrowserWindow.getAllWindows().length === 0) createAppWindow();
|
|
303
|
+
});
|
|
304
|
+
`);
|
|
305
|
+
writeFile(path.join(dir, 'src/main/preload.cts'), `import { contextBridge } from 'electron';
|
|
306
|
+
|
|
307
|
+
contextBridge.exposeInMainWorld('anhedral', {
|
|
308
|
+
platform: process.platform,
|
|
309
|
+
});
|
|
310
|
+
`);
|
|
311
|
+
writeFile(path.join(dir, 'index.html'), `<!doctype html>
|
|
312
|
+
<html lang="en">
|
|
313
|
+
<head>
|
|
314
|
+
<meta charset="UTF-8" />
|
|
315
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
316
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; base-uri 'none'; object-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https: ws://127.0.0.1:* wss:; img-src 'self' data: https:; font-src 'self' data: https:; frame-src https:; form-action 'self' https:" />
|
|
317
|
+
<title>${displayNameHtml}</title>
|
|
318
|
+
</head>
|
|
319
|
+
<body>
|
|
320
|
+
<div id="root"></div>
|
|
321
|
+
<script type="module" src="/src/renderer/main.tsx"></script>
|
|
322
|
+
</body>
|
|
323
|
+
</html>
|
|
324
|
+
`);
|
|
325
|
+
writeFile(path.join(dir, 'src/renderer/lib/utils.ts'), `import { clsx, type ClassValue } from 'clsx';
|
|
326
|
+
import { twMerge } from 'tailwind-merge';
|
|
327
|
+
|
|
328
|
+
export function cn(...inputs: ClassValue[]) {
|
|
329
|
+
return twMerge(clsx(inputs));
|
|
330
|
+
}
|
|
331
|
+
`);
|
|
332
|
+
writeFile(path.join(dir, 'src/renderer/components/ui/button.tsx'), `import * as React from 'react';
|
|
333
|
+
import { cn } from '@/lib/utils';
|
|
334
|
+
|
|
335
|
+
export function Button({ className, type = 'button', ...props }: React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
|
336
|
+
return (
|
|
337
|
+
<button
|
|
338
|
+
type={type}
|
|
339
|
+
className={cn('inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50', className)}
|
|
340
|
+
{...props}
|
|
341
|
+
/>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
`);
|
|
345
|
+
writeFile(path.join(dir, 'src/renderer/main.tsx'), `import React from 'react';
|
|
346
|
+
import ReactDOM from 'react-dom/client';
|
|
347
|
+
import './styles.css';
|
|
348
|
+
|
|
349
|
+
function App() {
|
|
350
|
+
return (
|
|
351
|
+
<main className="flex min-h-screen flex-col gap-6 bg-background p-8 text-foreground">
|
|
352
|
+
<section className="flex flex-col gap-3">
|
|
353
|
+
<h1 className="text-3xl font-semibold">{${displayNameLiteral}}</h1>
|
|
354
|
+
<p className="max-w-2xl text-muted-foreground">
|
|
355
|
+
${"Electron + shadcn/ui desktop client ready for your application."}
|
|
356
|
+
</p>
|
|
357
|
+
</section>
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
</main>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
367
|
+
<React.StrictMode>
|
|
368
|
+
<App />
|
|
369
|
+
</React.StrictMode>,
|
|
370
|
+
);
|
|
371
|
+
`);
|
|
372
|
+
writeFile(path.join(dir, 'src/renderer/styles.css'), `@import "tailwindcss";
|
|
373
|
+
|
|
374
|
+
:root {
|
|
375
|
+
--background: oklch(1 0 0);
|
|
376
|
+
--foreground: oklch(0.145 0 0);
|
|
377
|
+
--primary: oklch(0.205 0 0);
|
|
378
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
379
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
@theme inline {
|
|
383
|
+
--color-background: var(--background);
|
|
384
|
+
--color-foreground: var(--foreground);
|
|
385
|
+
--color-primary: var(--primary);
|
|
386
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
387
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
body {
|
|
391
|
+
margin: 0;
|
|
392
|
+
background: var(--background);
|
|
393
|
+
color: var(--foreground);
|
|
394
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
button {
|
|
398
|
+
border: 0;
|
|
399
|
+
}
|
|
400
|
+
`);
|
|
401
|
+
}
|