Skip to content

Commit 8526ad5

Browse files
author
shmuel hizmi
committed
master
1 parent a748f7c commit 8526ad5

10 files changed

Lines changed: 45 additions & 42 deletions

File tree

packages/fullstack/src/server/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type EventHandler = (...args: ReadonlyArray<SerializableValue>) => SerializableV
2828

2929
interface RegisteredEvent {
3030
readonly handler: EventHandler;
31-
readonly callbackDef?: CallbackDef;
31+
readonly callbackDef?: CallbackDef | undefined;
3232
}
3333

3434
interface AppProps {

packages/fullstack/src/shared/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ export interface Transport<Events extends object> {
1515
event: T,
1616
handler: (data: Events[T]) => void
1717
) => void;
18-
readonly off?: <T extends keyof Events>(
18+
readonly off?: (<T extends keyof Events>(
1919
event: T,
2020
handler: (data: Events[T]) => void
21-
) => void;
21+
) => void) | undefined;
2222
}
2323

2424
export interface EventResponseData {

packages/wmux-client/src/components/Sidebar.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,12 @@ function TabItem({
111111
return (
112112
<div
113113
onClick={onSelect}
114-
className={`flex items-center gap-2.5 pl-5 pr-2 py-[7px] cursor-pointer transition-colors rounded-sm mx-1 relative group ${
114+
className={`flex items-center gap-2.5 pl-5 pr-2 py-[7px] cursor-pointer transition-colors rounded-sm mx-1 group ${
115115
isActive
116116
? "bg-border/20 text-foreground"
117117
: "text-muted-foreground/50 hover:bg-border/10 hover:text-foreground/70"
118118
}`}
119119
>
120-
{isActive && (
121-
<div className="absolute left-0.5 top-1.5 bottom-1.5 w-0.5 bg-foreground/20 rounded-r" />
122-
)}
123-
124120
{TabIcon
125121
? <TabIcon size={14} className="shrink-0" />
126122
: <span className="w-[6px] h-[6px] rounded-full shrink-0" style={{ background: STATUS_COLORS[tab.status] ?? "#3f3f46" }} />
@@ -171,7 +167,10 @@ function CategorySection({
171167
const isFiles = category.type === "files";
172168

173169
return (
174-
<div className="border-b border-border/10 last:border-b-0">
170+
<div
171+
className="border-b border-border/10 last:border-b-0 border-l-2"
172+
style={{ borderLeftColor: category.color }}
173+
>
175174
<CategoryHeader
176175
category={category}
177176
isActive={isActive}

packages/wmux/src/components/FileViewerSession.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useEffect, useCallback, useRef, useImperativeHandle, forwardRef } from "react";
1+
import { useState, useEffect, useCallback, useRef, useImperativeHandle, forwardRef } from "react";
22
import { useViews } from "@playfast/echoform/server";
33
import { views } from "../views";
44
import { readdir } from "node:fs/promises";

packages/wmux/src/components/IframeSession.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import React from "react";
1+
import type { ReactElement } from "react";
22
import { useViews } from "@playfast/echoform/server";
33
import { views } from "../views";
44

55
export function IframeSession({ id, name, url }: {
66
readonly id: string;
77
readonly name: string;
88
readonly url: string;
9-
}): React.ReactElement | null {
9+
}): ReactElement | null {
1010
const View = useViews(views);
1111
if (!View) return null;
1212
return <View.WmuxIframe id={id} name={name} url={url} />;

packages/wmux/src/components/TerminalSession.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import React, { useEffect } from "react";
1+
import { useEffect, type ReactElement } from "react";
22
import { useViews, useStream } from "@playfast/echoform/server";
33
import { views, WmuxTerminal } from "../views";
44
import type { ManagedProcess } from "../process";
55
import { isCommandConfig } from "../types";
66

7-
export function TerminalSession({ proc }: { readonly proc: ManagedProcess }): React.ReactElement | null {
7+
export function TerminalSession({ proc }: { readonly proc: ManagedProcess }): ReactElement | null {
88
const View = useViews(views);
99
const output = useStream(WmuxTerminal, "output");
1010

@@ -24,8 +24,8 @@ export function TerminalSession({ proc }: { readonly proc: ManagedProcess }): Re
2424
name={proc.name}
2525
status={proc.status}
2626
output={output}
27-
onInput={(b64) => proc.write(b64)}
28-
onResize={({ cols, rows }) => proc.resize(cols, rows)}
27+
onInput={(b64: string) => proc.write(b64)}
28+
onResize={({ cols, rows }: { cols: number; rows: number }) => proc.resize(cols, rows)}
2929
/>
3030
);
3131
}

packages/wmux/src/components/WmuxRoot.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useEffect, useRef } from "react";
1+
import { createRef, useState, useEffect, useRef, type RefObject, type ReactElement } from "react";
22
import { useViews } from "@playfast/echoform/server";
33
import { views } from "../views";
44
import type { ManagedProcess } from "../process";
@@ -25,18 +25,18 @@ function categoryColor(name: string | undefined): string {
2525

2626
interface TabDef {
2727
readonly id: string;
28-
readonly description?: string;
29-
readonly icon?: string;
28+
readonly description?: string | undefined;
29+
readonly icon?: string | undefined;
3030
readonly tabType: "process" | "iframe";
31-
readonly url?: string;
31+
readonly url?: string | undefined;
3232
}
3333

3434
interface CategoryDef {
3535
readonly name: string;
36-
readonly icon?: string;
36+
readonly icon?: string | undefined;
3737
readonly type: "process" | "files";
3838
readonly tabs: readonly TabDef[];
39-
readonly fileRoot?: string;
39+
readonly fileRoot?: string | undefined;
4040
}
4141

4242
interface WmuxRootProps {
@@ -46,18 +46,18 @@ interface WmuxRootProps {
4646

4747
// ── Component ──
4848

49-
export function WmuxRoot({ processes, categoryDefs }: WmuxRootProps): React.ReactElement | null {
49+
export function WmuxRoot({ processes, categoryDefs }: WmuxRootProps): ReactElement | null {
5050
const View = useViews(views);
5151
const [activeCategory, setActiveCategory] = useState(categoryDefs[0]?.name ?? "");
5252
const [activeTabId, setActiveTabId] = useState("");
5353
const [statuses, setStatuses] = useState<Record<string, ProcessStatus>>({});
5454
const [fileStates, setFileStates] = useState<Record<string, FileViewerState>>({});
5555

5656
// File viewer action refs (one per file category)
57-
const fileRefs = useRef<Record<string, React.RefObject<FileViewerActions | null>>>({});
57+
const fileRefs = useRef<Record<string, RefObject<FileViewerActions | null>>>({});
5858
for (const def of categoryDefs) {
5959
if (def.type === "files" && !fileRefs.current[def.name]) {
60-
fileRefs.current[def.name] = React.createRef<FileViewerActions | null>();
60+
fileRefs.current[def.name] = createRef<FileViewerActions | null>();
6161
}
6262
}
6363

@@ -118,7 +118,7 @@ export function WmuxRoot({ processes, categoryDefs }: WmuxRootProps): React.Reac
118118
categories={categories}
119119
activeCategory={activeCategory}
120120
activeTabId={activeTabId}
121-
onSelectCategory={(cat) => {
121+
onSelectCategory={(cat: string) => {
122122
setActiveCategory(cat);
123123
const def = categoryDefs.find((d) => d.name === cat);
124124
if (!def) return;
@@ -130,12 +130,12 @@ export function WmuxRoot({ processes, categoryDefs }: WmuxRootProps): React.Reac
130130
}
131131
}}
132132
onSelectTab={setActiveTabId}
133-
onStartProcess={(id) => processes.get(id)?.start()}
134-
onStopProcess={(id) => processes.get(id)?.stop()}
135-
onRestartProcess={(id) => processes.get(id)?.restart()}
136-
onToggleDir={(path) => fileRefs.current[activeCategory]?.current?.toggleDir(path)}
137-
onOpenFile={(path) => fileRefs.current[activeCategory]?.current?.openFile(path)}
138-
onCloseFile={(id) => fileRefs.current[activeCategory]?.current?.closeFile(id)}
133+
onStartProcess={(id: string) => processes.get(id)?.start()}
134+
onStopProcess={(id: string) => processes.get(id)?.stop()}
135+
onRestartProcess={(id: string) => processes.get(id)?.restart()}
136+
onToggleDir={(path: string) => fileRefs.current[activeCategory]?.current?.toggleDir(path)}
137+
onOpenFile={(path: string) => fileRefs.current[activeCategory]?.current?.openFile(path)}
138+
onCloseFile={(id: string) => fileRefs.current[activeCategory]?.current?.closeFile(id)}
139139
>
140140
{[...processes.keys()].map((id) => (
141141
<TerminalSession key={id} proc={processes.get(id)!} />

packages/wmux/src/process.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ function createCommandProcess(
7777
const emitter = outputEmitter;
7878

7979
const proc = Bun.spawn(argv, {
80-
cwd: config.cwd,
81-
env: config.env ? { ...process.env, ...config.env } : undefined,
80+
...(config.cwd != null && { cwd: config.cwd }),
81+
...(config.env != null && { env: { ...process.env, ...config.env } }),
8282
terminal: {
8383
cols,
8484
rows,

packages/wmux/src/wmux.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import React from "react";
21
import { Render } from "@playfast/echoform-render";
32
import { Server } from "@playfast/echoform/server";
43
import { createManagedProcess, type ManagedProcess } from "./process";
54
import { createWmuxServer } from "./server";
65
import { generateToken } from "./token";
76
import { WmuxRoot } from "./components/WmuxRoot";
8-
import type { WmuxConfig, WmuxHandle, TabConfig } from "./types";
7+
import type { WmuxConfig, WmuxHandle } from "./types";
98

109
const BUILT_IN_CLIENT_URL = "https://wmux.play.fast";
1110

@@ -26,26 +25,26 @@ function openBrowser(url: string): void {
2625

2726
interface TabDef {
2827
readonly id: string;
29-
readonly description?: string;
30-
readonly icon?: string;
28+
readonly description?: string | undefined;
29+
readonly icon?: string | undefined;
3130
readonly tabType: "process" | "iframe";
32-
readonly url?: string;
31+
readonly url?: string | undefined;
3332
}
3433

3534
interface CategoryDef {
3635
readonly name: string;
37-
readonly icon?: string;
36+
readonly icon?: string | undefined;
3837
readonly type: "process" | "files";
3938
readonly tabs: readonly TabDef[];
40-
readonly fileRoot?: string;
39+
readonly fileRoot?: string | undefined;
4140
}
4241

4342
export async function wmux(config: WmuxConfig): Promise<WmuxHandle> {
4443
const port = config.port ?? 0;
4544
const hostname = config.hostname ?? "127.0.0.1";
4645
const token = config.token ?? generateToken();
4746
const clientUrl = config.clientUrl
48-
?? process.env.WMUX_CLIENT_URL
47+
?? process.env["WMUX_CLIENT_URL"]
4948
?? (BUILT_IN_CLIENT_URL.startsWith("__") ? "http://localhost:5173" : BUILT_IN_CLIENT_URL);
5049

5150
const processes = new Map<string, ManagedProcess>();

packages/wmux/tsconfig.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
"esModuleInterop": true,
1010
"skipLibCheck": true,
1111
"forceConsistentCasingInFileNames": true,
12+
"noUncheckedIndexedAccess": true,
13+
"noFallthroughCasesInSwitch": true,
14+
"exactOptionalPropertyTypes": true,
15+
"noPropertyAccessFromIndexSignature": true,
16+
"verbatimModuleSyntax": true,
1217
"types": ["bun"]
1318
},
1419
"include": ["src"]

0 commit comments

Comments
 (0)