@typed-assistant/builder 0.0.52 → 0.0.54

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typed-assistant/builder",
3
- "version": "0.0.52",
3
+ "version": "0.0.54",
4
4
  "exports": {
5
5
  "./appProcess": "./src/appProcess.tsx",
6
6
  "./bunInstall": "./src/bunInstall.tsx",
@@ -14,6 +14,7 @@
14
14
  "ignore": "^5.3.0",
15
15
  "react": "^18",
16
16
  "react-dom": "^18",
17
+ "tailwind-merge": "^2.2.1",
17
18
  "zod": "^3.22.4"
18
19
  },
19
20
  "devDependencies": {
@@ -24,10 +25,10 @@
24
25
  "home-assistant-js-websocket": "^8.2.0",
25
26
  "ts-toolbelt": "^9.6.0",
26
27
  "typescript": "^5.3.3",
27
- "@typed-assistant/eslint-config": "0.0.8",
28
- "@typed-assistant/logger": "0.0.16",
29
- "@typed-assistant/typescript-config": "0.0.8",
30
- "@typed-assistant/utils": "0.0.14"
28
+ "@typed-assistant/eslint-config": "0.0.9",
29
+ "@typed-assistant/logger": "0.0.17",
30
+ "@typed-assistant/typescript-config": "0.0.9",
31
+ "@typed-assistant/utils": "0.0.15"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "home-assistant-js-websocket": "^8.2.0"
@@ -40,6 +40,13 @@ export async function setup({
40
40
  startWebappServer({
41
41
  basePath,
42
42
  getSubprocesses: () => subprocesses,
43
+ onRestartAppRequest: async () => {
44
+ subprocesses = await killAndRestartApp(
45
+ entryFile,
46
+ { mdiPaths },
47
+ subprocesses,
48
+ )
49
+ },
43
50
  })
44
51
  setupWatcher({
45
52
  directoryToWatch,
@@ -57,11 +57,13 @@ let lastMessage = ""
57
57
  export const startWebappServer = async ({
58
58
  basePath,
59
59
  getSubprocesses,
60
+ onRestartAppRequest,
60
61
  }: {
61
62
  basePath: string
62
63
  getSubprocesses: () => {
63
64
  app: Subprocess<"ignore", "pipe", "pipe">
64
65
  }
66
+ onRestartAppRequest: () => void
65
67
  }) => {
66
68
  const buildResult = await Bun.build({
67
69
  entrypoints: [tsEntryPoint],
@@ -105,6 +107,10 @@ export const startWebappServer = async ({
105
107
  headers: { "content-type": "text/html" },
106
108
  }),
107
109
  )
110
+ .get("/restart-app", async () => {
111
+ onRestartAppRequest()
112
+ return { message: "Restarting app..." }
113
+ })
108
114
  .get("/restart-addon", async () => {
109
115
  await killSubprocess(getSubprocesses().app)
110
116
  restartAddon()
@@ -7,7 +7,7 @@ const App = () => {
7
7
  return (
8
8
  <div className="grid md:grid-cols-3">
9
9
  <div className="col-span-2">
10
- <Terminal basePath={basePath} />
10
+ <Terminal />
11
11
  </div>
12
12
  <div className="col-span-1">
13
13
  <Logs basePath={basePath} />
@@ -1,11 +1,75 @@
1
- import { useCallback, useState } from "react"
1
+ import { useCallback, useEffect, useState } from "react"
2
2
  import { app } from "./api"
3
3
  import { useWS } from "./useWS"
4
4
  import { WSIndicator } from "./WSIndicator"
5
5
  import { AppSection } from "./AppSection"
6
6
  import { buttonStyle } from "./styles"
7
+ import { twMerge } from "tailwind-merge"
7
8
 
8
- export function Terminal({ basePath }: { basePath: string }) {
9
+ type ButtonAsyncProps = {
10
+ onClick: () => Promise<{ error?: unknown }>
11
+ stateLabels: {
12
+ idle: string
13
+ loading?: string
14
+ error?: string
15
+ success?: string
16
+ }
17
+ }
18
+
19
+ const ButtonAsync = ({
20
+ onClick,
21
+ stateLabels: stateLabelsProp,
22
+ }: ButtonAsyncProps) => {
23
+ const stateLabels = {
24
+ error: "Error",
25
+ loading: "Loading...",
26
+ success: "Success",
27
+ ...stateLabelsProp,
28
+ }
29
+ const [state, setState] = useState<"idle" | "loading" | "error" | "success">(
30
+ "idle",
31
+ )
32
+
33
+ useEffect(() => {
34
+ if (state !== "loading" && state !== "idle") {
35
+ const timeout = setTimeout(() => setState("idle"), 2000)
36
+
37
+ return () => {
38
+ clearTimeout(timeout)
39
+ }
40
+ }
41
+ }, [state])
42
+
43
+ const handleClick = async () => {
44
+ if (state !== "idle") return
45
+ setState("loading")
46
+
47
+ const { error } = await onClick()
48
+
49
+ if (error) {
50
+ setState("error")
51
+ return
52
+ }
53
+
54
+ setState("success")
55
+ }
56
+
57
+ return (
58
+ <button
59
+ className={twMerge(
60
+ buttonStyle,
61
+ state === "loading" && "bg-blue-300 text-blue-800",
62
+ state === "error" && "bg-red-300 text-red-800",
63
+ state === "success" && "bg-green-300 text-green-800",
64
+ )}
65
+ onClick={handleClick}
66
+ >
67
+ {stateLabels[state]}
68
+ </button>
69
+ )
70
+ }
71
+
72
+ export function Terminal() {
9
73
  const [content, setContent] = useState("")
10
74
  const ws = useWS({
11
75
  subscribe: useCallback(() => app.ws.subscribe(), []),
@@ -20,9 +84,22 @@ export function Terminal({ basePath }: { basePath: string }) {
20
84
  TypedAssistant <WSIndicator ws={ws.ws} />
21
85
  </h1>
22
86
  <div className="flex flex-wrap gap-2">
23
- <a className={buttonStyle} href={`${basePath}/restart-addon`}>
24
- Restart addon
25
- </a>
87
+ <ButtonAsync
88
+ onClick={() => app["restart-app"].get()}
89
+ stateLabels={{
90
+ idle: "Restart app",
91
+ loading: "Restarting app...",
92
+ success: "App restarted",
93
+ }}
94
+ />
95
+ <ButtonAsync
96
+ onClick={() => app["restart-addon"].get()}
97
+ stateLabels={{
98
+ idle: "Restart addon",
99
+ loading: "Restarting addon...",
100
+ success: "Addon restarted",
101
+ }}
102
+ />
26
103
  </div>
27
104
  </>
28
105
  )}
@@ -31,3 +108,53 @@ export function Terminal({ basePath }: { basePath: string }) {
31
108
  </AppSection>
32
109
  )
33
110
  }
111
+
112
+ const RestartAppButton = () => {
113
+ const [state, setState] = useState<"idle" | "loading" | "error" | "success">(
114
+ "idle",
115
+ )
116
+
117
+ useEffect(() => {
118
+ if (state !== "loading" && state !== "idle") {
119
+ const timeout = setTimeout(() => setState("idle"), 2000)
120
+
121
+ return () => {
122
+ clearTimeout(timeout)
123
+ }
124
+ }
125
+ }, [state])
126
+
127
+ const onClick = async () => {
128
+ if (state !== "idle") return
129
+ setState("loading")
130
+
131
+ const { data, error } = await app["restart-app"].get()
132
+
133
+ if (error) {
134
+ setState("error")
135
+ return
136
+ }
137
+
138
+ setState("success")
139
+ }
140
+
141
+ return (
142
+ <button
143
+ className={twMerge(
144
+ buttonStyle,
145
+ state === "loading" && "bg-blue-500",
146
+ state === "error" && "bg-red-500",
147
+ state === "success" && "bg-green-500",
148
+ )}
149
+ onClick={onClick}
150
+ >
151
+ {state === "loading"
152
+ ? "Restarting app..."
153
+ : state === "error"
154
+ ? "Error restarting app"
155
+ : state === "success"
156
+ ? "App restarted"
157
+ : "Restart app"}
158
+ </button>
159
+ )
160
+ }