diff --git a/TODO.txt b/TODO.txt
index 80e7bb5..e1e9730 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -9,6 +9,13 @@
[x] watch each app and restart it on update
[x] watches for and adds/removes apps
+## apps
+
+[ ] truism
+[ ] big clock
+[ ] shrimp repl(?)
+[ ] dungeon party
+
## cli
[ ] `toes --help`
@@ -21,6 +28,7 @@
## webui
-[ ] list projects
+[x] list projects
+[ ] start/stop/restart project
[ ] todo.txt
[ ] ...
diff --git a/apps/profile/package.json b/apps/profile/package.json
index e8be2ee..7f8ee73 100644
--- a/apps/profile/package.json
+++ b/apps/profile/package.json
@@ -15,7 +15,7 @@
"typescript": "^5.9.2"
},
"dependencies": {
- "hype": "git+https://git.nose.space/defunkt/hype",
- "forge": "git+https://git.nose.space/defunkt/forge"
+ "forge": "git+https://git.nose.space/defunkt/forge",
+ "hype": "git+https://git.nose.space/defunkt/hype"
}
}
diff --git a/bun.lock b/bun.lock
index 5d6f287..bde2aa7 100644
--- a/bun.lock
+++ b/bun.lock
@@ -16,17 +16,17 @@
},
},
"packages": {
- "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
+ "@types/bun": ["@types/bun@1.3.7", "", { "dependencies": { "bun-types": "1.3.7" } }, "sha512-lmNuMda+Z9b7tmhA0tohwy8ZWFSnmQm1UDWXtH5r9F7wZCfkeO3Jx7wKQ1EOiKq43yHts7ky6r8SDJQWRNupkA=="],
"@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
- "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
+ "bun-types": ["bun-types@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-qyschsA03Qz+gou+apt6HNl6HnI+sJJLL4wLDke4iugsE6584CMupOtTY1n+2YC9nGVrEKUlTs99jjRLKgWnjQ=="],
"forge": ["forge@git+https://git.nose.space/defunkt/forge#9b6e1e91ec77d7e03589cac256d97fb9cd942184", { "peerDependencies": { "typescript": "^5" } }, "9b6e1e91ec77d7e03589cac256d97fb9cd942184"],
"hono": ["hono@4.11.7", "", {}, "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw=="],
- "hype": ["hype@git+https://git.nose.space/defunkt/hype#52086f4eb94cc36ecd9470d9a101ff01002687c8", { "dependencies": { "hono": "^4.10.4", "kleur": "^4.1.5" }, "peerDependencies": { "typescript": "^5" } }, "52086f4eb94cc36ecd9470d9a101ff01002687c8"],
+ "hype": ["hype@git+https://git.nose.space/defunkt/hype#7b9cade936c4897539d2ca14299d90f80deb6ebe", { "dependencies": { "hono": "^4.10.4", "kleur": "^4.1.5" }, "peerDependencies": { "typescript": "^5" } }, "7b9cade936c4897539d2ca14299d90f80deb6ebe"],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index fcd0541..860bdce 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -1,8 +1,40 @@
+import { define, Styles } from 'forge'
import { runningApps } from '../server/apps'
+const Apps = define('Apps', {
+ margin: '0 auto',
+ width: 750,
+ paddingTop: 20,
+})
+
+const color = '#00c0c9'
+const hoverColor = 'magenta'
+const Link = define({
+ base: 'a',
+ color,
+ textDecoration: 'none',
+ borderBottom: `1px solid ${color}`,
+ selectors: {
+ '&:hover': {
+ color: hoverColor,
+ cursor: 'pointer'
+ }
+ }
+})
+
+const Timestamp = define({
+ fontSize: 18
+})
+
export default () => (
- <>
+
+
🐾 Running Apps
- {runningApps().map(app => )}
- >
+ {runningApps().map(app => (
+
+ {app.port}: {app.name}
+ Started: {new Date(app.started).toLocaleString()}
+
+ ))}
+
)
\ No newline at end of file
diff --git a/src/server/apps.ts b/src/server/apps.ts
index eb8afc7..f992191 100644
--- a/src/server/apps.ts
+++ b/src/server/apps.ts
@@ -7,6 +7,7 @@ const APPS_DIR = join(process.env.DATA_DIR ?? '.', 'apps')
type RunningApp = {
name: string
port: number
+ started: number
proc: Subprocess
}
@@ -25,6 +26,7 @@ export const appNames = () => {
return readdirSync(APPS_DIR, { withFileTypes: true })
.filter(e => e.isDirectory())
.map(e => e.name)
+ .filter(isApp)
.sort()
}
@@ -33,7 +35,6 @@ const getPort = () => NEXT_PORT++
export const runApps = () => {
for (const dir of appNames()) {
- if (!isApp(dir)) continue
const port = getPort()
runApp(dir, port)
}
@@ -86,7 +87,12 @@ const runApp = async (dir: string, port: number) => {
stderr: 'pipe',
})
- _runningApps.set(dir, { name: dir, port, proc })
+ _runningApps.set(dir, {
+ name: dir,
+ port,
+ proc,
+ started: Date.now()
+ })
const streamOutput = async (stream: ReadableStream | null, isErr: boolean) => {
if (!stream) return
@@ -116,9 +122,8 @@ const runApp = async (dir: string, port: number) => {
})
}
-export const runningApps = () =>
+export const runningApps = (): RunningApp[] =>
Array.from(_runningApps.values())
- .map(({ name, port }) => ({ name, port }))
.sort((a, b) => a.port - b.port)
const stopApp = (dir: string) => {