codehost 0.21.0 → 0.22.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [0.22.0](https://github.com/snomiao/codehost/compare/v0.21.0...v0.22.0) (2026-06-14)
2
+
3
+
4
+ ### Features
5
+
6
+ * **web:** "Set up a machine" card on the home page ([c2cec58](https://github.com/snomiao/codehost/commit/c2cec580695cf759a986b18fc8fedd1494fc542f))
7
+
1
8
  # [0.21.0](https://github.com/snomiao/codehost/compare/v0.20.5...v0.21.0) (2026-06-13)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codehost",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -196,6 +196,50 @@ function RoomClient(props: {
196
196
  return null;
197
197
  }
198
198
 
199
+ /** A copy-to-clipboard command row: label, the command, and a Copy button. */
200
+ function CopyCommand({ label, command }: { label: string; command: string }) {
201
+ const [copied, setCopied] = useState(false);
202
+ const copy = async () => {
203
+ try {
204
+ await navigator.clipboard.writeText(command);
205
+ } catch {
206
+ // clipboard blocked (insecure context / permission) — fall back to prompt
207
+ window.prompt("Copy this command:", command);
208
+ }
209
+ setCopied(true);
210
+ setTimeout(() => setCopied(false), 1500);
211
+ };
212
+ return (
213
+ <div style={styles.cmdRow}>
214
+ <span style={styles.cmdLabel}>{label}</span>
215
+ <code style={styles.cmdCode}>{command}</code>
216
+ <button style={styles.cmdCopy} onClick={copy}>
217
+ {copied ? "Copied!" : "Copy"}
218
+ </button>
219
+ </div>
220
+ );
221
+ }
222
+
223
+ /**
224
+ * "Set up a machine" card: the one-liner that turns any machine into a codehost
225
+ * server. The script bootstraps everything (Bun, the CLI, VS Code, the daemon),
226
+ * so the user needs no prerequisites — not even Bun. setup.sh/.ps1 are aliases
227
+ * of install.* served by Pages (see public/_redirects).
228
+ */
229
+ function SetupCard() {
230
+ return (
231
+ <div style={styles.setupCard}>
232
+ <div style={styles.setupHead}>Set up a machine</div>
233
+ <p style={styles.setupSub}>
234
+ Run this on a machine to serve it here. It installs everything — Bun, VS Code, and the
235
+ codehost daemon — no prerequisites, and it picks a token and opens the browser for you.
236
+ </p>
237
+ <CopyCommand label="macOS / Linux" command="curl -fsSL https://codehost.dev/setup.sh | sh" />
238
+ <CopyCommand label="Windows" command={'powershell -c "irm codehost.dev/setup.ps1 | iex"'} />
239
+ </div>
240
+ );
241
+ }
242
+
199
243
  export function Discovery() {
200
244
  // Joined rooms — each token *is* a room id, and we keep one live signaling
201
245
  // client per room (see RoomClient). Seeded from the persisted room list plus
@@ -1070,12 +1114,10 @@ export function Discovery() {
1070
1114
  </span>
1071
1115
  )}
1072
1116
  </div>
1073
- {tokens.length === 0 && <p style={styles.dim}>Join a room to see your workspaces.</p>}
1074
1117
  {tokens.length > 0 && serverCount === 0 && (
1075
- <p style={styles.dim}>
1076
- No servers online. Run <code style={styles.code}>bunx codehost serve -t &lt;token&gt;</code> on a machine.
1077
- </p>
1118
+ <p style={styles.dim}>No servers online in your rooms yet.</p>
1078
1119
  )}
1120
+ {serverCount === 0 && <SetupCard />}
1079
1121
  {serverCount > 0 && (
1080
1122
  <>
1081
1123
  <input
@@ -1225,6 +1267,10 @@ export function Discovery() {
1225
1267
  </p>
1226
1268
  </section>
1227
1269
  )}
1270
+
1271
+ {/* When servers already exist the empty-state card is hidden, so keep an
1272
+ "add another machine" affordance available down here. */}
1273
+ {serverCount > 0 && <SetupCard />}
1228
1274
  </main>
1229
1275
  </div>
1230
1276
  </>
@@ -1312,6 +1358,13 @@ const styles: Record<string, React.CSSProperties> = {
1312
1358
  echoBad: { marginTop: 6, fontSize: 12, color: "#f48771", fontFamily: "monospace" },
1313
1359
  rosterSection: { marginTop: 28 },
1314
1360
  rosterHead: { fontSize: 14, color: "#aaa", fontWeight: 600, margin: "0 0 12px" },
1361
+ setupCard: { marginTop: 20, background: "#252525", border: "1px solid #3d3d3d", borderRadius: 8, padding: "16px 18px" },
1362
+ setupHead: { fontSize: 15, color: "#fff", fontWeight: 600, marginBottom: 6 },
1363
+ setupSub: { fontSize: 13, color: "#aaa", margin: "0 0 14px", lineHeight: 1.5 },
1364
+ cmdRow: { display: "flex", alignItems: "center", gap: 10, marginTop: 8 },
1365
+ cmdLabel: { fontSize: 11, color: "#888", width: 88, flexShrink: 0 },
1366
+ cmdCode: { flex: 1, minWidth: 0, background: "#1b1b1b", border: "1px solid #3d3d3d", borderRadius: 6, padding: "8px 10px", fontFamily: "monospace", fontSize: 12.5, color: "#dcdcaa", overflow: "auto", whiteSpace: "nowrap" },
1367
+ cmdCopy: { flexShrink: 0, background: "#0e639c", border: "none", color: "#fff", padding: "8px 12px", borderRadius: 6, cursor: "pointer", fontSize: 12 },
1315
1368
  rosterHint: { margin: "10px 0 0", fontSize: 12, color: "#888" },
1316
1369
  personRow: { display: "flex", alignItems: "center", gap: 10, background: "#252525", border: "1px solid #3d3d3d", borderRadius: 8, padding: "8px 14px", fontSize: 13 },
1317
1370
  personDot: { color: "#4ec9b0", fontSize: 10 },