diff --git a/src/client/index.tsx b/src/client/index.tsx
index 41e8a67..206ce7b 100644
--- a/src/client/index.tsx
+++ b/src/client/index.tsx
@@ -340,10 +340,14 @@ const toggleSidebar = () => {
const OpenEmojiPicker = define('OpenEmojiPicker', {
cursor: 'pointer',
- render({ props, parts: { Root } }) {
+ render({ props: { app, children }, parts: { Root } }) {
return openEmojiPicker((emoji) => {
- console.log('Selected:', emoji)
- })}>{props.children}
+ if (!app) return
+
+ fetch(`/api/apps/${app.name}/icon?icon=${emoji}`, { method: 'POST' })
+ app.icon = emoji
+ render()
+ })}>{children}
}
})
@@ -351,7 +355,7 @@ const AppDetail = ({ app }: { app: App }) => (
<>
- {app.icon ?? }
+ {app.icon ?? }
{app.name}
diff --git a/src/server/apps.ts b/src/server/apps.ts
index f47585d..9c8e4ef 100644
--- a/src/server/apps.ts
+++ b/src/server/apps.ts
@@ -1,5 +1,5 @@
import type { Subprocess } from 'bun'
-import { existsSync, readdirSync, readFileSync, watch } from 'fs'
+import { existsSync, readdirSync, readFileSync, writeFileSync, watch } from 'fs'
import { join } from 'path'
import type { App as SharedApp, AppState, LogLine } from '../shared/types'
@@ -91,6 +91,20 @@ const loadApp = (dir: string): LoadResult => {
}
}
+const saveApp = (dir: string, pkg: any) => {
+ const path = join(APPS_DIR, dir, 'package.json')
+ writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n')
+}
+
+export const updateAppIcon = (dir: string, icon: string) => {
+ const { pkg, error } = loadApp(dir)
+ if (error) throw new Error(error)
+
+ pkg.toes ??= {}
+ pkg.toes.icon = icon
+ saveApp(dir, pkg)
+}
+
const isApp = (dir: string): boolean =>
!loadApp(dir).error
diff --git a/src/server/index.tsx b/src/server/index.tsx
index 032eb32..3e2ff4f 100644
--- a/src/server/index.tsx
+++ b/src/server/index.tsx
@@ -1,5 +1,5 @@
import { Hype } from 'hype'
-import { allApps, initApps, onChange, startApp, stopApp } from './apps'
+import { allApps, initApps, onChange, startApp, stopApp, updateAppIcon } from './apps'
import type { App as SharedApp } from '../shared/types'
const app = new Hype({ layout: false })
@@ -75,4 +75,17 @@ app.post('/api/apps/:app/stop', c => {
return c.json({ ok: true })
})
+app.post('/api/apps/:app/icon', c => {
+ const appName = c.req.param('app')
+ const icon = c.req.query('icon') ?? ''
+ if (!icon) return c.json({ error: 'No icon query param provided' })
+
+ try {
+ updateAppIcon(appName, icon)
+ return c.json({ ok: true })
+ } catch (error) {
+ return c.json({ error })
+ }
+})
+
export default app.defaults