sesame-kit 0.4.1 → 0.5.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/README.ja.md +106 -73
- package/README.md +76 -43
- package/clients/js/sesame-client.mjs +2 -2
- package/clients/python/__pycache__/sesame_client.cpython-313.pyc +0 -0
- package/clients/python/sesame_client.py +2 -2
- package/docs/{architecture.md → en/architecture.md} +7 -7
- package/docs/en/ble.md +65 -0
- package/docs/{commands.md → en/commands.md} +26 -11
- package/docs/en/index.md +20 -0
- package/docs/en/integration.md +181 -0
- package/docs/{library.md → en/library.md} +6 -5
- package/docs/en/migration.md +13 -0
- package/docs/en/quickstart.md +51 -0
- package/docs/{architecture.ja.md → ja/architecture.md} +37 -37
- package/docs/ja/ble.md +65 -0
- package/docs/{commands.ja.md → ja/commands.md} +54 -39
- package/docs/ja/index.md +20 -0
- package/docs/ja/integration.md +181 -0
- package/docs/{library.ja.md → ja/library.md} +6 -5
- package/docs/ja/migration.md +13 -0
- package/docs/ja/quickstart.md +51 -0
- package/package.json +6 -2
- package/src/cli/serve.js +1 -1
- package/src/cli.js +14 -12
- package/src/client.js +2 -2
- package/src/config.js +1 -1
- package/src/paths.js +5 -5
- package/src/session-ui.js +85 -52
- package/src/tokens.js +1 -1
- package/types/cli.d.ts.map +1 -1
- package/types/client.d.ts +1 -1
- package/types/session-ui.d.ts.map +1 -1
- package/docs/migration.ja.md +0 -13
- package/docs/migration.md +0 -13
package/src/session-ui.js
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
// 状態が変わった瞬間に描き直す Ink (React for CLI) で実装する。BLE の mechStatus publish や
|
|
6
6
|
// バックグラウンド接続の完了を `bus` の "update" イベントで受け、React state を更新して再描画する。
|
|
7
7
|
//
|
|
8
|
+
// 操作: ↑↓ 移動 / → か Enter で決定 / ← か Esc で戻る / q で終了。
|
|
9
|
+
// アクション実行後はホームに戻らず、その操作メニューに留まる (続けて操作できる)。
|
|
10
|
+
//
|
|
8
11
|
// JSX は使わない (本リポは src を素の ESM で実行しビルド工程が無いため)。React.createElement = h。
|
|
9
12
|
|
|
10
13
|
import React from "react";
|
|
@@ -43,6 +46,7 @@ export function SessionApp({ devices, hasCloud, bus, exec, actionsFor, fmtState,
|
|
|
43
46
|
const [numVal, setNumVal] = React.useState(""); // autolock 秒数 / LED duty 入力
|
|
44
47
|
const [selRemote, setSelRemote] = React.useState(null); // IR: 選択中リモコン
|
|
45
48
|
const [irKeys, setIrKeys] = React.useState(null); // IR: 取得したキー一覧 (null=未取得)
|
|
49
|
+
const [hi, setHi] = React.useState(null); // → 決定用: ハイライト中の項目
|
|
46
50
|
|
|
47
51
|
// BLE onStatus / 背景接続完了 → 再描画。
|
|
48
52
|
React.useEffect(() => {
|
|
@@ -51,18 +55,82 @@ export function SessionApp({ devices, hasCloud, bus, exec, actionsFor, fmtState,
|
|
|
51
55
|
return () => bus.off("update", on);
|
|
52
56
|
}, [bus]);
|
|
53
57
|
|
|
58
|
+
// hi (→ 決定用ハイライト) は各 SelectInput の onHighlight が先頭項目で更新する。
|
|
59
|
+
// SelectInput を描画しない可能性のある mode (空の IR リスト) に入る時だけ明示的にクリアし、
|
|
60
|
+
// 前メニューの項目が残って → が誤爆しないようにする (下の selectAction / selectIrRemote 参照)。
|
|
61
|
+
|
|
54
62
|
const backToActions = () => { setMode("actions"); };
|
|
55
63
|
|
|
56
|
-
//
|
|
64
|
+
// ---- 各メニューの選択ハンドラ (SelectInput.onSelect と → 決定の両方から呼ぶ) ----
|
|
65
|
+
const selectDevice = (it) => {
|
|
66
|
+
if (!it) return;
|
|
67
|
+
if (it.value === "__quit") { exit(); return; }
|
|
68
|
+
setSelName(it.value); setMsg(""); setMode("actions");
|
|
69
|
+
};
|
|
70
|
+
const selectAction = (it) => {
|
|
71
|
+
if (!it) return;
|
|
72
|
+
if (it.value === "__back") { if (single) exit(); else { setMode("devices"); setMsg(""); } return; }
|
|
73
|
+
if (it.value === "autolock") { setNumVal(""); setMode("autolock"); return; }
|
|
74
|
+
if (it.value === "led") { setNumVal(""); setMode("led"); return; }
|
|
75
|
+
if (it.value === "ir") { setHi(null); setSelRemote(null); setIrKeys(null); setMode("ir-remote"); return; }
|
|
76
|
+
runExec(it.value, devices.get(selName));
|
|
77
|
+
};
|
|
78
|
+
const selectIrRemote = (it) => {
|
|
79
|
+
if (!it) return;
|
|
80
|
+
if (it.value === "__back") { backToActions(); return; }
|
|
81
|
+
setHi(null); setSelRemote(it.value); setIrKeys(null); setMode("ir-key");
|
|
82
|
+
Promise.resolve(listKeysFor ? listKeysFor(it.value) : []).then(setIrKeys).catch(() => setIrKeys([]));
|
|
83
|
+
};
|
|
84
|
+
const selectIrKey = (it) => {
|
|
85
|
+
if (!it) return;
|
|
86
|
+
if (it.value === "__back") { setMode("ir-remote"); return; }
|
|
87
|
+
runExec("ir", devices.get(selName), { remote: selRemote, key: it.value });
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// ← / Esc で 1 つ戻る。Esc は最上位 (devices / single の actions) では終了する。
|
|
91
|
+
// ← は最上位では何もしない (誤操作で終了しないように)。
|
|
92
|
+
const goBack = (allowExitAtTop) => {
|
|
93
|
+
if (mode === "actions") {
|
|
94
|
+
if (single) { if (allowExitAtTop) exit(); }
|
|
95
|
+
else { setMode("devices"); setMsg(""); }
|
|
96
|
+
} else if (mode === "ir-key") setMode("ir-remote");
|
|
97
|
+
else if (mode === "devices") { if (allowExitAtTop) exit(); }
|
|
98
|
+
else backToActions(); // autolock / led / ir-remote
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// 現在 mode のメニュー項目 (render と → 決定で共有。順序が両者で一致する)。
|
|
102
|
+
const menuItems = () => {
|
|
103
|
+
if (mode === "devices") return [...names.map((n) => ({ label: n, value: n })), { label: "終了", value: "__quit" }];
|
|
104
|
+
if (mode === "actions") return [...actionsFor(devices.get(selName)), { label: single ? "終了" : "← 戻る", value: "__back" }];
|
|
105
|
+
if (mode === "ir-remote") {
|
|
106
|
+
const r = hub3RemotesFor ? hub3RemotesFor(devices.get(selName)) : [];
|
|
107
|
+
return r.length ? [...r, { label: "← 戻る", value: "__back" }] : [];
|
|
108
|
+
}
|
|
109
|
+
if (mode === "ir-key") return (irKeys && irKeys.length) ? [...irKeys, { label: "← 戻る", value: "__back" }] : [];
|
|
110
|
+
return [];
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// → で決定。ink-select-input の onHighlight は初期項目では発火しないため、移動前は hi=null。
|
|
114
|
+
// その場合は先頭 (= 既定でハイライトされている項目) にフォールバックする。
|
|
115
|
+
const goForward = () => {
|
|
116
|
+
const it = hi || menuItems()[0];
|
|
117
|
+
if (!it) return;
|
|
118
|
+
if (mode === "devices") selectDevice(it);
|
|
119
|
+
else if (mode === "actions") selectAction(it);
|
|
120
|
+
else if (mode === "ir-remote") selectIrRemote(it);
|
|
121
|
+
else if (mode === "ir-key") selectIrKey(it);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const isList = mode === "devices" || mode === "actions" || mode === "ir-remote" || mode === "ir-key";
|
|
125
|
+
|
|
57
126
|
useInput((input, key) => {
|
|
58
127
|
if (mode === "busy") return;
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
128
|
+
// q は**メニュー系のみ**で終了。autolock/LED の数値入力中は文字として TextInput へ渡す。
|
|
129
|
+
if (input === "q" && isList) { exit(); return; }
|
|
130
|
+
if (key.escape) { goBack(true); return; } // Esc: 戻る (最上位では終了)
|
|
131
|
+
// ← / → は数値入力 (autolock/led) ではテキストカーソル移動に使うので、リスト系のみで奪う。
|
|
132
|
+
if (isList && key.leftArrow) { goBack(false); return; } // ←: 戻る (最上位では何もしない)
|
|
133
|
+
if (isList && key.rightArrow) { goForward(); return; } // →: 決定
|
|
66
134
|
});
|
|
67
135
|
|
|
68
136
|
const runExec = (op, d, extra) => {
|
|
@@ -70,10 +138,10 @@ export function SessionApp({ devices, hasCloud, bus, exec, actionsFor, fmtState,
|
|
|
70
138
|
exec(op, d, extra)
|
|
71
139
|
.then((m) => setMsg(m))
|
|
72
140
|
.catch((e) => setMsg(`error: ${e?.message || e}`))
|
|
73
|
-
.finally(() => setMode(
|
|
141
|
+
.finally(() => setMode("actions")); // ホームに戻らず操作メニューに留まる (続けて操作できる)
|
|
74
142
|
};
|
|
75
143
|
|
|
76
|
-
// ヘッダ: 全デバイスの現在状態 (ライブ)
|
|
144
|
+
// ヘッダ: 全デバイスの現在状態 (ライブ) + 操作ヒント。
|
|
77
145
|
const header = h(
|
|
78
146
|
Box,
|
|
79
147
|
{ flexDirection: "column" },
|
|
@@ -85,6 +153,7 @@ export function SessionApp({ devices, hasCloud, bus, exec, actionsFor, fmtState,
|
|
|
85
153
|
return h(Text, { key: n, color: d.ble ? "green" : undefined },
|
|
86
154
|
` ${n} [${label}·${tag}]: ${fmtState(d)}`);
|
|
87
155
|
}),
|
|
156
|
+
h(Text, { dimColor: true }, " ↑↓ 移動 → 決定 ← 戻る q 終了"),
|
|
88
157
|
msg ? h(Text, { color: "yellow" }, msg) : null,
|
|
89
158
|
);
|
|
90
159
|
const box = (...kids) => h(Box, { flexDirection: "column" }, header, ...kids);
|
|
@@ -116,66 +185,30 @@ export function SessionApp({ devices, hasCloud, bus, exec, actionsFor, fmtState,
|
|
|
116
185
|
const d = devices.get(selName);
|
|
117
186
|
const remotes = (hub3RemotesFor ? hub3RemotesFor(d) : []);
|
|
118
187
|
if (remotes.length === 0) {
|
|
119
|
-
return box(h(Text, null, `${selName}: 登録リモコンがありません ( sesame remote add で登録 )
|
|
188
|
+
return box(h(Text, null, `${selName}: 登録リモコンがありません ( sesame remote add で登録 )。← / Esc で戻る`));
|
|
120
189
|
}
|
|
121
|
-
const items = [...remotes, { label: "← 戻る", value: "__back" }];
|
|
122
190
|
return box(h(Text, null, `${selName} の IR: リモコン選択`),
|
|
123
|
-
h(SelectInput, {
|
|
124
|
-
items,
|
|
125
|
-
onSelect: (it) => {
|
|
126
|
-
if (it.value === "__back") { backToActions(); return; }
|
|
127
|
-
setSelRemote(it.value); setIrKeys(null); setMode("ir-key");
|
|
128
|
-
Promise.resolve(listKeysFor ? listKeysFor(it.value) : []).then(setIrKeys).catch(() => setIrKeys([]));
|
|
129
|
-
},
|
|
130
|
-
}),
|
|
191
|
+
h(SelectInput, { items: menuItems(), onHighlight: setHi, onSelect: selectIrRemote }),
|
|
131
192
|
);
|
|
132
193
|
}
|
|
133
194
|
|
|
134
195
|
// IR: キー選択 (非同期取得中はローディング表示)。
|
|
135
196
|
if (mode === "ir-key") {
|
|
136
|
-
const d = devices.get(selName);
|
|
137
197
|
if (irKeys === null) return box(h(Text, null, `${selRemote}: キー取得中...`));
|
|
138
|
-
if (irKeys.length === 0) return box(h(Text, null, `${selRemote}: キーがありません ( sesame remote sync-keys )
|
|
139
|
-
const items = [...irKeys, { label: "← 戻る", value: "__back" }];
|
|
198
|
+
if (irKeys.length === 0) return box(h(Text, null, `${selRemote}: キーがありません ( sesame remote sync-keys )。← / Esc で戻る`));
|
|
140
199
|
return box(h(Text, null, `${selRemote} のキー選択 (送信)`),
|
|
141
|
-
h(SelectInput, {
|
|
142
|
-
items,
|
|
143
|
-
onSelect: (it) => {
|
|
144
|
-
if (it.value === "__back") { setMode("ir-remote"); return; }
|
|
145
|
-
runExec("ir", d, { remote: selRemote, key: it.value });
|
|
146
|
-
},
|
|
147
|
-
}),
|
|
200
|
+
h(SelectInput, { items: menuItems(), onHighlight: setHi, onSelect: selectIrKey }),
|
|
148
201
|
);
|
|
149
202
|
}
|
|
150
203
|
|
|
151
204
|
if (mode === "actions") {
|
|
152
|
-
const d = devices.get(selName);
|
|
153
|
-
const items = [...actionsFor(d)];
|
|
154
|
-
items.push({ label: single ? "終了" : "← 戻る", value: "__back" });
|
|
155
205
|
return box(h(Text, null, `${selName} の操作:`),
|
|
156
|
-
h(SelectInput, {
|
|
157
|
-
items,
|
|
158
|
-
onSelect: (it) => {
|
|
159
|
-
if (it.value === "__back") { if (single) exit(); else { setMode("devices"); setMsg(""); } return; }
|
|
160
|
-
if (it.value === "autolock") { setNumVal(""); setMode("autolock"); return; }
|
|
161
|
-
if (it.value === "led") { setNumVal(""); setMode("led"); return; }
|
|
162
|
-
if (it.value === "ir") { setSelRemote(null); setIrKeys(null); setMode("ir-remote"); return; }
|
|
163
|
-
runExec(it.value, d);
|
|
164
|
-
},
|
|
165
|
-
}),
|
|
206
|
+
h(SelectInput, { items: menuItems(), onHighlight: setHi, onSelect: selectAction }),
|
|
166
207
|
);
|
|
167
208
|
}
|
|
168
209
|
|
|
169
210
|
// mode === "devices"
|
|
170
|
-
const items = names.map((n) => ({ label: n, value: n }));
|
|
171
|
-
items.push({ label: "終了", value: "__quit" });
|
|
172
211
|
return box(h(Text, null, "操作するデバイス:"),
|
|
173
|
-
h(SelectInput, {
|
|
174
|
-
items,
|
|
175
|
-
onSelect: (it) => {
|
|
176
|
-
if (it.value === "__quit") { exit(); return; }
|
|
177
|
-
setSelName(it.value); setMsg(""); setMode("actions");
|
|
178
|
-
},
|
|
179
|
-
}),
|
|
212
|
+
h(SelectInput, { items: menuItems(), onHighlight: setHi, onSelect: selectDevice }),
|
|
180
213
|
);
|
|
181
214
|
}
|
package/src/tokens.js
CHANGED
|
@@ -17,7 +17,7 @@ function writeJson(path, data) {
|
|
|
17
17
|
// - Windows では fs.writeFileSync の mode は read-only flag に degrade される
|
|
18
18
|
// - mkdirSync の mode は **新規作成時のみ** 適用される (既存ディレクトリの
|
|
19
19
|
// パーミッションは変わらない)。旧バージョンで 0755 で作られたディレクトリは
|
|
20
|
-
// `chmod 700 ~/.config/sesame-
|
|
20
|
+
// `chmod 700 ~/.config/sesame-kit` で手動修正が必要。
|
|
21
21
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
22
22
|
writeFileSync(path, JSON.stringify(data, null, 2) + "\n", { mode: 0o600 });
|
|
23
23
|
}
|
package/types/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.js"],"names":[],"mappings":"AA+9CA,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.js"],"names":[],"mappings":"AA+9CA,oDAuQC"}
|
package/types/client.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-ui.d.ts","sourceRoot":"","sources":["../src/session-ui.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session-ui.d.ts","sourceRoot":"","sources":["../src/session-ui.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;GAUG;AACH,oCATW;IACN,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE;QAAC,KAAK,EAAC,MAAM,CAAC;QAAC,GAAG,EAAC,MAAM,GAAC,IAAI,CAAA;KAAC,CAAC,CAAC;IACtD,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,EAAE,OAAO,aAAa,EAAE,YAAY,CAAC;IACxC,IAAI,EAAE,CAAC,EAAE,EAAC,MAAM,EAAE,MAAM,EAAC,MAAM,EAAE,OAAO,CAAC,EAAC,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,UAAU,EAAE,CAAC,MAAM,EAAC,MAAM,KAAG,KAAK,CAAC;QAAC,KAAK,EAAC,MAAM,CAAC;QAAC,KAAK,EAAC,MAAM,CAAA;KAAC,CAAC,CAAC;IACjE,QAAQ,EAAE,CAAC,MAAM,EAAC,MAAM,KAAG,MAAM,CAAC;CACnC,iBAKH;AAED;;;;;;;;;QAkLC"}
|
package/docs/migration.ja.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!-- [English](./migration.md) | 日本語 -->
|
|
2
|
-
|
|
3
|
-
# 旧版からのマイグレーション
|
|
4
|
-
|
|
5
|
-
> English: [migration.md](./migration.md)
|
|
6
|
-
|
|
7
|
-
旧 `sesame-hub3` (CLI=`hub3-ir`) からのアップグレード:
|
|
8
|
-
|
|
9
|
-
- CLI 名は `sesame` に変更 (旧 `hub3-ir` は廃止)。シェルスクリプトを使っている場合は置換。
|
|
10
|
-
- 設定ディレクトリ (`~/.config/sesame-hub3`) は変更なし、既存 config.json はそのまま使える。
|
|
11
|
-
- `locks` キーは自動で追加される (空 `{}` から開始)。`sesame locks sync-from-devices` で `devices` コマンドの出力から取り込み可能。
|
|
12
|
-
|
|
13
|
-
旧 `.env + .tokens.json + keys.json` からの移行は `sesame migrate` がそのまま使える。
|
package/docs/migration.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!-- English | [日本語](./migration.ja.md) -->
|
|
2
|
-
|
|
3
|
-
# Migrating from older versions
|
|
4
|
-
|
|
5
|
-
> 日本語: [migration.ja.md](./migration.ja.md)
|
|
6
|
-
|
|
7
|
-
Upgrading from the old `sesame-hub3` (CLI = `hub3-ir`):
|
|
8
|
-
|
|
9
|
-
- The CLI is renamed to `sesame` (the old `hub3-ir` is removed). Replace it in any shell scripts.
|
|
10
|
-
- The config directory (`~/.config/sesame-hub3`) is unchanged; an existing config.json works as-is.
|
|
11
|
-
- The `locks` key is added automatically (starting from an empty `{}`). Import from the `devices` command output with `sesame locks sync-from-devices`.
|
|
12
|
-
|
|
13
|
-
Migrating from an old `.env` + `.tokens.json` + `keys.json` works with `sesame migrate`.
|