walover-line-harness-gui 0.4.0 → 0.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "walover-line-harness-gui",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "LINE Harness のセットアップを、ターミナルを開かずにブラウザから行うためのローカルツール",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,7 +14,7 @@ import {
14
14
  } from "./state.js";
15
15
  import { ptyBackend } from "./pty.js";
16
16
  import { getVersionStatus } from "./cli-version.js";
17
- import { checkNodeRuntime } from "./runtime.js";
17
+ import { checkNodeRuntime, ptyProblem } from "./runtime.js";
18
18
  import { checkLeftoverState, leftoverWarning } from "./leftover.js";
19
19
 
20
20
  const HERE = dirname(fileURLToPath(import.meta.url));
@@ -115,14 +115,18 @@ async function route(req, res, pathname, method) {
115
115
 
116
116
  if (pathname === "/api/health" && method === "GET") {
117
117
  const backend = ptyBackend();
118
+ const node = checkNodeRuntime();
119
+
118
120
  // **始めても無駄な条件は、始める前に返す。**
119
- // 擬似端末が無い / Node が機械に合っていない、はどちらも必ず失敗する
120
- json(res, 200, {
121
- ok: true,
122
- pty: backend !== null,
123
- ptyBackend: backend,
124
- node: checkNodeRuntime(),
125
- });
121
+ // 擬似端末が無い / Node が機械に合っていない、はどちらも必ず失敗する。
122
+ //
123
+ // 形を揃えて返すのは、画面が同じ出し方をできるようにするため。
124
+ // どれも「そのまま貼れる文章」を持っていて、参加者はコピーするだけでよい
125
+ const blockers = [];
126
+ if (backend === null) blockers.push(ptyProblem());
127
+ if (node.problem) blockers.push(node.problem);
128
+
129
+ json(res, 200, { ok: true, pty: backend !== null, ptyBackend: backend, node, blockers });
126
130
  return true;
127
131
  }
128
132
 
@@ -21,6 +21,20 @@
21
21
 
22
22
  import { cpus } from "node:os";
23
23
 
24
+ /**
25
+ * 参加者に渡す「助けを求める文章」。
26
+ *
27
+ * **読者は非開発者。** 何が悪いかを説明しても、直し方が分からなければ先に進めない。
28
+ * 実機で「Silicon のエラー文は非エンジニアにはハードルが高い」という指摘を受けた。
29
+ *
30
+ * そこで、**そのまま Claude Code などに貼れる文章**を用意する。
31
+ * 症状・機械の情報・やってほしいこと・壊してはいけないものを、こちらで書いておく。
32
+ * 参加者がやるのは「コピーして貼る」だけにする。
33
+ *
34
+ * @param {string[]} lines
35
+ */
36
+ const prompt = (lines) => lines.join("\n");
37
+
24
38
  /**
25
39
  * @typedef {object} RuntimeCheck
26
40
  * @property {boolean} ok
@@ -33,6 +47,7 @@ import { cpus } from "node:os";
33
47
  * @property {string} title
34
48
  * @property {string} body
35
49
  * @property {string[]} fix 直し方。**既存の mise / nvm を壊さない手順**にする
50
+ * @property {string} prompt そのまま貼れる、助けを求める文章
36
51
  */
37
52
 
38
53
  /**
@@ -55,18 +70,85 @@ export function checkNodeRuntime(override = {}) {
55
70
  cpu,
56
71
  problem: {
57
72
  id: "x64-on-apple-silicon",
58
- title: "この Node ではセットアップできません",
73
+ title: "このパソコンでは、このままでは始められません",
59
74
  body:
60
- `この Mac は ${cpu}(Apple Silicon)ですが、いま動いている Node は x86_64 版です。` +
61
- "このまま進めると、途中まで作られたあとで必ず失敗します" +
62
- "(本家が使う esbuild が x86_64 版を選び、Rosetta 上で落ちるため)。",
75
+ `お使いの Mac は ${cpu} ですが、いま動いている Node.js が、この Mac の種類に合っていません。` +
76
+ "このまま進めると、途中まで作られたあとで必ず失敗します。" +
77
+ "先に Node.js を入れ替える必要があります。",
63
78
  fix: [
64
- "nodejs.org から macOS の ARM64 版(LTS)を入れてください。",
65
- "いま使っている mise や nvm の設定は変えなくて構いません。" +
66
- "別の場所に置いて、そこから起動すれば足ります。",
67
- "例: ~/.local/node-arm64 に展開したなら、" +
68
- "~/.local/node-arm64/bin/npx walover-line-harness-gui で起動する",
79
+ "下の文章をコピーして、Claude Code(または詳しい方)に渡してください。",
80
+ "自分で直す場合は、nodejs.org の macOS「ARM64」版(LTS)を入れてください。",
69
81
  ],
82
+ prompt: prompt([
83
+ "Mac で LINE Harness のセットアップ画面を開いたら、",
84
+ "「このパソコンでは、このままでは始められません」と出て先に進めません。",
85
+ "",
86
+ "画面にはこう書かれています:",
87
+ ` お使いの Mac は ${cpu} ですが、いま動いている Node.js が、この Mac の種類に合っていません。`,
88
+ "",
89
+ "調べたところ、こうなっているようです:",
90
+ ` ・Node.js のバージョン: ${process.version}`,
91
+ ` ・Node.js の種類: ${arch}(この Mac に合っているのは arm64)`,
92
+ "",
93
+ "この Mac に合った Node.js(arm64 版)を用意して、",
94
+ "セットアップ画面を起動し直せるようにしてください。",
95
+ "",
96
+ "お願いしたい条件:",
97
+ "・いま入っている mise / nvm / Homebrew の設定は壊さないでください",
98
+ "・新しい Node.js は別の場所に入れて、そこから起動する形にしてください",
99
+ "・終わったら、起動するためのコマンドを1行で教えてください",
100
+ " (いまは npx walover-line-harness-gui で起動しています)",
101
+ ]),
70
102
  },
71
103
  };
72
104
  }
105
+
106
+ /**
107
+ * 擬似端末を用意できない場合。
108
+ *
109
+ * 本家 CLI は実際の端末を要求するので、これが無いと起動すらできない。
110
+ * **参加者から見れば「始められない」だけ**なので、言い方も直し方もそれに合わせる。
111
+ *
112
+ * @param {{ platform?: string }} [override] テスト用
113
+ * @returns {RuntimeProblem}
114
+ */
115
+ export function ptyProblem(override = {}) {
116
+ const platform = override.platform ?? process.platform;
117
+
118
+ // 直し方は OS で違う。macOS 以外に xcode-select を案内しても意味がない
119
+ const fix =
120
+ platform === "darwin"
121
+ ? [
122
+ "下の文章をコピーして、Claude Code(または詳しい方)に渡してください。",
123
+ "自分で直す場合は、ターミナルで xcode-select --install を実行してください。",
124
+ ]
125
+ : [
126
+ "下の文章をコピーして、Claude Code(または詳しい方)に渡してください。",
127
+ "自分で直す場合は、いったんターミナルを終了して、もう一度起動してみてください。",
128
+ ];
129
+
130
+ return {
131
+ id: "no-pty",
132
+ title: "このパソコンでは、このままでは始められません",
133
+ body:
134
+ "セットアップを進めるのに必要な部品が、このパソコンで用意できませんでした。" +
135
+ "このままでは、セットアップツールを起動できません。",
136
+ fix,
137
+ prompt: prompt([
138
+ "LINE Harness のセットアップ画面を開いたら、",
139
+ "「このパソコンでは、このままでは始められません」と出て先に進めません。",
140
+ "",
141
+ "画面にはこう書かれています:",
142
+ " セットアップを進めるのに必要な部品が、このパソコンで用意できませんでした。",
143
+ "",
144
+ "このツールは擬似端末(PTY)を使います。",
145
+ `私のパソコンは ${platform} です。`,
146
+ "擬似端末を使えるようにして、セットアップ画面を起動し直せるようにしてください。",
147
+ "",
148
+ "お願いしたい条件:",
149
+ "・いま入っている開発ツールの設定は壊さないでください",
150
+ "・終わったら、起動するためのコマンドを1行で教えてください",
151
+ " (いまは npx walover-line-harness-gui で起動しています)",
152
+ ]),
153
+ };
154
+ }
@@ -73,6 +73,18 @@
73
73
  .note .note-detail { margin: .4rem 0 0 1.1rem; padding-left: .7rem;
74
74
  border-left: 2px solid var(--line); white-space: pre-line; }
75
75
 
76
+ /* 「そのまま貼れる文章」。読ませるより、コピーさせることが目的 */
77
+ .helpme { margin-top: .8rem; padding-top: .8rem; border-top: 1px solid var(--err-line); }
78
+ .helpme-lead { margin: 0 0 .4rem; font-weight: 600; font-size: .88rem; }
79
+ .helpme-text { margin: 0 0 .5rem; padding: .7rem .8rem; max-height: 11rem; overflow-y: auto;
80
+ background: var(--bg); border: 1px solid var(--line); border-radius: .4rem;
81
+ color: var(--fg); font-size: .8rem; line-height: 1.6;
82
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
83
+ white-space: pre-wrap; word-break: break-word; }
84
+ .helpme button { font: inherit; font-size: .85rem; font-weight: 600; padding: .45rem 1.1rem;
85
+ border: 0; border-radius: 999px; cursor: pointer;
86
+ background: var(--brand-fill); color: var(--brand-fill-fg); }
87
+
76
88
  .status { display: flex; align-items: center; gap: .5rem; font-size: .85rem; color: var(--muted);
77
89
  margin-bottom: 1.5rem; }
78
90
  .dot { width: .55rem; height: .55rem; border-radius: 50%; background: var(--muted); flex: none; }
@@ -290,32 +302,32 @@
290
302
  *
291
303
  * 見つけたら**開始させない**。途中まで作られてから失敗すると、
292
304
  * Cloudflare 上に中途半端なものが残り、やり直しのコストが高い。
305
+ *
306
+ * **読者は非開発者。** 原因を説明しても直し方が分からなければ先に進めないので、
307
+ * **そのまま貼れる文章**をコピーできる形で添える(実機での指摘)。
293
308
  */
294
- const blockers = [];
295
-
296
- if (health.pty === false) {
297
- // 擬似端末が無いと本家 CLI は起動できない。値を入れてもらう前に伝える
298
- blockers.push("擬似端末を用意できていません");
299
- banner(
300
- "セットアップを開始できません",
301
- "擬似端末を用意できませんでした(python3 が見つかりません)。" +
302
- "macOS では、ターミナルで xcode-select --install を実行すると入ります。",
303
- true,
304
- );
305
- }
309
+ const blockers = (health.blockers ?? []).map((b) => b.title);
310
+ for (const problem of health.blockers ?? []) $("banners").append(renderBlocker(problem));
306
311
 
307
- // Apple Silicon で x86_64 の Node を使っている場合。
308
- // 進めると D1 を作ったあとで必ず失敗する(実測)
309
- if (health.node && health.node.ok === false) {
310
- const { title, body, fix } = health.node.problem;
311
- blockers.push(title);
312
- const node = el("div", { className: "banner bad" });
313
- node.append(el("strong", { textContent: title }));
314
- node.append(document.createTextNode(body));
315
- const list = el("ul");
316
- for (const line of fix) list.append(el("li", { textContent: line }));
317
- node.append(list);
318
- $("banners").append(node);
312
+ function renderBlocker({ title, body, fix, prompt }) {
313
+ const box = el("div", { className: "banner bad" });
314
+ box.append(el("strong", { textContent: title }));
315
+ box.append(document.createTextNode(body));
316
+
317
+ if (fix?.length) {
318
+ const list = el("ul");
319
+ for (const line of fix) list.append(el("li", { textContent: line }));
320
+ box.append(list);
321
+ }
322
+
323
+ if (prompt) {
324
+ const help = el("div", { className: "helpme" });
325
+ help.append(el("p", { className: "helpme-lead", textContent: "そのままコピーして貼ってください:" }));
326
+ help.append(el("pre", { className: "helpme-text", textContent: prompt }));
327
+ help.append(copyButton(prompt, "この文章をコピー"));
328
+ box.append(help);
329
+ }
330
+ return box;
319
331
  }
320
332
 
321
333
  // 本家 CLI の版のズレ。
@@ -870,7 +882,13 @@
870
882
  row.append(reveal);
871
883
  }
872
884
 
873
- const copy = el("button", { type: "button", textContent: "コピー" });
885
+ row.append(copyButton(value, "コピー"));
886
+ return row;
887
+ }
888
+
889
+ /** 押すと中身を写すボタン。押したことが分かるように文字を変える */
890
+ function copyButton(value, label) {
891
+ const copy = el("button", { type: "button", textContent: label });
874
892
  copy.addEventListener("click", async () => {
875
893
  try {
876
894
  // http://127.0.0.1 は secure context なので clipboard が使える
@@ -886,12 +904,11 @@
886
904
  copy.textContent = "コピーしました";
887
905
  copy.classList.add("done");
888
906
  setTimeout(() => {
889
- copy.textContent = "コピー";
907
+ copy.textContent = label;
890
908
  copy.classList.remove("done");
891
909
  }, 1600);
892
910
  });
893
- row.append(copy);
894
- return row;
911
+ return copy;
895
912
  }
896
913
 
897
914
  /**