Compare commits

...

2 Commits

Author SHA1 Message Date
5d898ac485 fix dir mk/rm 2026-01-28 20:44:34 -08:00
a44c85abe0 default_emoji 2026-01-28 19:13:18 -08:00
2 changed files with 39 additions and 9 deletions

View File

@ -9,6 +9,8 @@ import { openEmojiPicker } from './tags/emoji-picker'
let selectedApp: string | null = localStorage.getItem('selectedApp')
let sidebarCollapsed: boolean = localStorage.getItem('sidebarCollapsed') === 'true'
const DEFAULT_EMOJI = '🖥️'
// Server state (from SSE)
let apps: App[] = []
@ -380,7 +382,7 @@ const AppDetail = ({ app }: { app: App }) => (
<>
<MainHeader>
<MainTitle>
<OpenEmojiPicker app={app}>{app.icon ?? <StatusDot state={app.state} />}</OpenEmojiPicker>
<OpenEmojiPicker app={app}>{app.icon ?? DEFAULT_EMOJI}</OpenEmojiPicker>
&nbsp;
{app.name}
</MainTitle>
@ -400,7 +402,6 @@ const AppDetail = ({ app }: { app: App }) => (
<InfoValue>
<StatusDot state={app.state} />
{stateLabels[app.state]}
{app.port ? ` on :${app.port}` : ''}
</InfoValue>
</InfoRow>
{app.state === 'running' && app.port && (
@ -413,6 +414,14 @@ const AppDetail = ({ app }: { app: App }) => (
</InfoValue>
</InfoRow>
)}
{app.state === 'running' && app.port && (
<InfoRow>
<InfoLabel>Port</InfoLabel>
<InfoValue>
{app.port}
</InfoValue>
</InfoRow>
)}
{app.started && (
<InfoRow>
<InfoLabel>Started</InfoLabel>
@ -500,12 +509,15 @@ const Dashboard = () => {
style={sidebarCollapsed ? { justifyContent: 'center', padding: '10px 12px' } : undefined}
title={sidebarCollapsed ? app.name : undefined}
>
{app.state === 'running' && app.icon ? (
<span style={{ fontSize: sidebarCollapsed ? 18 : 14 }}>{app.icon}</span>
{sidebarCollapsed ? (
<span style={{ fontSize: 18 }}>{app.icon ?? DEFAULT_EMOJI}</span>
) : (
<StatusDot state={app.state} />
<>
<span style={{ fontSize: 14 }}>{app.icon ?? DEFAULT_EMOJI}</span>
{app.name}
<StatusDot state={app.state} style={{ marginLeft: 'auto' }} />
</>
)}
{!sidebarCollapsed && app.name}
</AppItem>
))}
</AppList>

View File

@ -1,5 +1,8 @@
import type { Subprocess } from 'bun'
import { existsSync, readdirSync, readFileSync, writeFileSync, watch } from 'fs'
import {
existsSync, readdirSync, readFileSync, writeFileSync,
statSync, watch
} from 'fs'
import { join } from 'path'
import type { App as SharedApp, AppState, LogLine } from '../shared/types'
@ -208,9 +211,8 @@ export const stopApp = (dir: string) => {
}
const watchAppsDir = () => {
watch(APPS_DIR, { recursive: true }, (_event, filename) => {
watch(APPS_DIR, { recursive: true }, (event, filename) => {
if (!filename) return
if (!filename.includes('/')) return
// Extract the app directory name from the path (e.g., "myapp/package.json" -> "myapp")
const dir = filename.split('/')[0]!
@ -230,6 +232,13 @@ const watchAppsDir = () => {
const app = _apps.get(dir)!
// check if app was deleted
if (!isDir(join(APPS_DIR, dir))) {
_apps.delete(dir)
update()
return
}
// Only care about package.json changes for existing apps
if (!filename.endsWith('package.json')) return
@ -256,6 +265,7 @@ const watchAppsDir = () => {
app.state = 'invalid'
update()
}
if (!error && app.state === 'invalid') {
app.state = 'stopped'
update()
@ -263,6 +273,14 @@ const watchAppsDir = () => {
})
}
function isDir(path: string): boolean {
try {
return statSync(path).isDirectory()
} catch {
return false
}
}
export const initApps = () => {
discoverApps()
runApps()