feedeas 0.1.0-alpha.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 +15 -0
- package/bun.lock +392 -0
- package/dist/cli/index.js +4720 -0
- package/dist/ui/assets/index-CVEIKd3u.js +14 -0
- package/dist/ui/assets/index-cRTfA0UF.css +1 -0
- package/dist/ui/index.html +14 -0
- package/index.ts +1 -0
- package/package.json +37 -0
- package/reference_app_ignore_this.tsx +544 -0
- package/src/cli/index.ts +67 -0
- package/src/cli/server/api.ts +57 -0
- package/src/cli/server/index.ts +53 -0
- package/src/ui/App.tsx +544 -0
- package/src/ui/index.css +1 -0
- package/src/ui/index.html +13 -0
- package/src/ui/main.tsx +10 -0
- package/tsconfig.json +27 -0
- package/vite.config.ts +22 -0
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { createServer } from './server/index';
|
|
4
|
+
import open from 'open';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
const program = new Command();
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.name('feedeas')
|
|
16
|
+
.description('Feedeas CLI - Start your local editor')
|
|
17
|
+
.version('0.0.1');
|
|
18
|
+
|
|
19
|
+
program
|
|
20
|
+
.command('start')
|
|
21
|
+
.description('Start the Feedeas server in the current directory')
|
|
22
|
+
.option('-p, --port <number>', 'Port to run on', '3000')
|
|
23
|
+
.option('--no-open', 'Do not open browser')
|
|
24
|
+
.action(async (options) => {
|
|
25
|
+
const port = parseInt(options.port);
|
|
26
|
+
const cwd = process.cwd();
|
|
27
|
+
|
|
28
|
+
console.log(`Starting Feedeas in ${cwd}...`);
|
|
29
|
+
|
|
30
|
+
// Determine static root
|
|
31
|
+
// Try resolving dist/ui relative to this file
|
|
32
|
+
// In dev: src/cli/index.ts -> ../../dist/ui
|
|
33
|
+
// In prod: dist/cli/index.js -> ../ui
|
|
34
|
+
|
|
35
|
+
let staticRoot = path.resolve(__dirname, '../../dist/ui');
|
|
36
|
+
if (!fs.existsSync(staticRoot)) {
|
|
37
|
+
staticRoot = path.resolve(__dirname, '../ui');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Fallback for when running from project root in dev without build
|
|
41
|
+
if (!fs.existsSync(staticRoot)) {
|
|
42
|
+
// Maybe we are in dev and want to point to src/ui? No, we serve built assets.
|
|
43
|
+
// User must build ui first.
|
|
44
|
+
console.warn(`Warning: UI assets not found at ${staticRoot}. Did you run 'bun run build'?`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const app = createServer(staticRoot);
|
|
48
|
+
|
|
49
|
+
const server = Bun.serve({
|
|
50
|
+
port,
|
|
51
|
+
fetch: app.fetch,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const url = `http://localhost:${port}`;
|
|
55
|
+
console.log(`Server running at ${url}`);
|
|
56
|
+
|
|
57
|
+
if (options.open) {
|
|
58
|
+
await open(url);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Default action
|
|
63
|
+
if (!process.argv.slice(2).length) {
|
|
64
|
+
program.parse(['start'], { from: 'user' });
|
|
65
|
+
} else {
|
|
66
|
+
program.parse(process.argv);
|
|
67
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
const api = new Hono();
|
|
6
|
+
|
|
7
|
+
// Helper to get absolute path
|
|
8
|
+
const getPath = (cwd: string, path: string) => join(cwd, path);
|
|
9
|
+
|
|
10
|
+
// List files
|
|
11
|
+
api.get('/fs/list', async (c) => {
|
|
12
|
+
const cwd = process.cwd();
|
|
13
|
+
// Optional: Accept a query param for subdirectory
|
|
14
|
+
try {
|
|
15
|
+
const files = await readdir(cwd, { withFileTypes: true });
|
|
16
|
+
const result = files.map(f => ({
|
|
17
|
+
name: f.name,
|
|
18
|
+
isDirectory: f.isDirectory(),
|
|
19
|
+
size: 0 // Simplification for now
|
|
20
|
+
}));
|
|
21
|
+
return c.json({ path: cwd, files: result });
|
|
22
|
+
} catch (e: any) {
|
|
23
|
+
return c.json({ error: e.message }, 500);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Read file
|
|
28
|
+
api.get('/fs/read', async (c) => {
|
|
29
|
+
const path = c.req.query('path');
|
|
30
|
+
if (!path) return c.json({ error: 'Path required' }, 400);
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const fullPath = getPath(process.cwd(), path);
|
|
34
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
35
|
+
return c.json({ content });
|
|
36
|
+
} catch (e: any) {
|
|
37
|
+
return c.json({ error: e.message }, 500);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Write file
|
|
42
|
+
api.post('/fs/write', async (c) => {
|
|
43
|
+
const body = await c.req.json();
|
|
44
|
+
const { path, content } = body;
|
|
45
|
+
|
|
46
|
+
if (!path || content === undefined) return c.json({ error: 'Path and content required' }, 400);
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const fullPath = getPath(process.cwd(), path);
|
|
50
|
+
await writeFile(fullPath, content, 'utf-8');
|
|
51
|
+
return c.json({ success: true });
|
|
52
|
+
} catch (e: any) {
|
|
53
|
+
return c.json({ error: e.message }, 500);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export default api;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { serveStatic } from 'hono/bun';
|
|
3
|
+
import { cors } from 'hono/cors';
|
|
4
|
+
import api from './api';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
const app = new Hono();
|
|
9
|
+
|
|
10
|
+
// CORS is helpful if we run UI on different port in dev
|
|
11
|
+
app.use('/*', cors());
|
|
12
|
+
|
|
13
|
+
// Mount API
|
|
14
|
+
app.route('/api', api);
|
|
15
|
+
|
|
16
|
+
// Serve Static UI
|
|
17
|
+
// We need to determine where 'dist/ui' is located relative to this file
|
|
18
|
+
// In dev: src/cli/server/index.ts -> ../../../dist/ui
|
|
19
|
+
// In prod: dist/cli/index.js (bundled) -> ../ui
|
|
20
|
+
// But if bundled, __dirname might be part of the bundle?
|
|
21
|
+
// Actually, Bun.resolveSync or checking relative paths is safer.
|
|
22
|
+
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = path.dirname(__filename);
|
|
25
|
+
|
|
26
|
+
// Simple heuristic: check if ../../../dist/ui exists (dev), else ../ui (prod)
|
|
27
|
+
// Actually, for simplicity, we will assume we are running from project root in dev,
|
|
28
|
+
// or installed globally.
|
|
29
|
+
// Let's pass the static root from the caller (CLI entry point) to be sure.
|
|
30
|
+
|
|
31
|
+
export const createServer = (staticRoot: string) => {
|
|
32
|
+
const app = new Hono();
|
|
33
|
+
|
|
34
|
+
app.use('/*', cors());
|
|
35
|
+
app.route('/api', api);
|
|
36
|
+
|
|
37
|
+
// Serve static files
|
|
38
|
+
app.use('/*', serveStatic({
|
|
39
|
+
root: staticRoot,
|
|
40
|
+
// If file not found, serve index.html for SPA
|
|
41
|
+
onNotFound: (path, c) => {
|
|
42
|
+
return c.req.path.startsWith('/api') ? undefined : void 0;
|
|
43
|
+
}
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// Fallback to index.html for SPA routing
|
|
47
|
+
app.get('*', serveStatic({
|
|
48
|
+
root: staticRoot,
|
|
49
|
+
path: 'index.html'
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
return app;
|
|
53
|
+
}
|