dsh-mishu 0.5.4 → 0.7.1

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/lib/client.js CHANGED
@@ -33,7 +33,7 @@ __export(index_exports, {
33
33
  module.exports = __toCommonJS(index_exports);
34
34
 
35
35
  // src/client/Board.tsx
36
- var import_react3 = require("react");
36
+ var import_react5 = require("react");
37
37
 
38
38
  // src/client/api.ts
39
39
  async function post(path, body) {
@@ -70,6 +70,13 @@ var settingsApi = {
70
70
  testGithub: (lang) => post("/github/test", { lang }),
71
71
  saveToken: (lang, token) => post("/github/token/save", { lang, token }),
72
72
  deleteToken: (lang) => post("/github/token/delete", { lang }),
73
+ saveNotify: (lang, notify) => post("/notify/save", { lang, notify }),
74
+ testNotify: (lang, channel) => post("/notify/test", { lang, channel }),
75
+ saveFeishu: (lang, webhook, secret) => post("/feishu/save", { lang, webhook, secret }),
76
+ deleteFeishu: (lang) => post("/feishu/delete", { lang }),
77
+ deviceStart: (lang) => post("/github/device/start", { lang }),
78
+ deviceStatus: (lang) => post("/github/device/status", { lang }),
79
+ deviceCancel: (lang) => post("/github/device/cancel", { lang }),
73
80
  saveMail: (lang, account, password) => post("/mail/save", { lang, account, password }),
74
81
  testMail: (lang, account, password) => post("/mail/test", { lang, account, password }),
75
82
  deleteMail: (lang, id) => post("/mail/delete", { lang, id }),
@@ -142,6 +149,9 @@ var zh = {
142
149
  restartTip: "\u91CD\u88C5\u6216\u5347\u7EA7\u63D2\u4EF6\u540E\uFF0C\u53EF\u4EE5\u7528\u8FD9\u4E2A\u6309\u952E\u91CD\u542F\u672C DSH",
143
150
  restartConfirm: "\u91CD\u542F DSH\uFF1F\u5B89\u88C5\u63D2\u4EF6\u6216\u8005\u63D2\u4EF6\u66F4\u65B0\u540E\u9700\u8981\u91CD\u542F DSH\u3002",
144
151
  restarting: "\u6B63\u5728\u91CD\u542F\u2026\u7A97\u53E3\u4F1A\u5173\u6389\u518D\u81EA\u52A8\u6253\u5F00",
152
+ restartingWeb: "\u6B63\u5728\u91CD\u542F\u2026\u9875\u9762\u4F1A\u65AD\u5F00\u4E00\u4F1A\u513F\uFF0C\u8FDE\u4E0A\u540E\u81EA\u52A8\u5237\u65B0",
153
+ restartReloading: "\u5DF2\u7ECF\u91CD\u542F\u597D\u4E86\uFF0C\u6B63\u5728\u5237\u65B0\u9875\u9762\u2026",
154
+ restartSlow: "\u4E24\u5206\u949F\u4E86\u8FD8\u6CA1\u8FDE\u4E0A\uFF0C\u8BF7\u624B\u52A8\u5237\u65B0\u9875\u9762\u770B\u770B\u3002",
145
155
  all: "\u5168\u90E8",
146
156
  sourceGithub: "GitHub",
147
157
  sourceMail: "\u90AE\u4EF6",
@@ -159,6 +169,19 @@ var zh = {
159
169
  sTokenGh: "\u7528 GitHub CLI\uFF08gh\uFF09\u767B\u5F55\u7684\u8D26\u53F7\uFF0Ctoken \u5728\u7CFB\u7EDF\u94A5\u5319\u4E32\u91CC",
160
170
  sTokenKeychain: "\u4F60\u5728\u8FD9\u91CC\u586B\u7684 token\uFF0C\u5B58\u5728\u7CFB\u7EDF\u94A5\u5319\u4E32\u91CC",
161
171
  sTokenPaste: "\u586B\u4E00\u4E2A GitHub token",
172
+ gLogin: "\u7528 GitHub \u767B\u5F55",
173
+ gPasteInstead: "\u6216\u8005\u7C98\u8D34\u4E00\u4E2A token",
174
+ gStarting: "\u6B63\u5728\u5411 GitHub \u8981\u767B\u5F55\u7801\u2026",
175
+ gStep1: "1. \u590D\u5236\u8FD9\u4E2A\u767B\u5F55\u7801",
176
+ gCopy: "\u590D\u5236",
177
+ gCopied: "\u5DF2\u590D\u5236",
178
+ gStep2: "2. \u6253\u5F00 GitHub\uFF0C\u7C98\u8D34\u767B\u5F55\u7801\uFF0C\u70B9 Authorize\u3002\u7528\u624B\u673A\u6216\u522B\u7684\u7535\u8111\u6253\u5F00\u4E5F\u884C\u3002",
179
+ gOpen: "\u6253\u5F00 GitHub",
180
+ gWaiting: "\u7B49\u4F60\u5728 GitHub \u4E0A\u6388\u6743\u2026",
181
+ gScopes: "\u4F1A\u8BF7\u6C42\u4E24\u9879\u6743\u9650\uFF1A\u8BFB\u901A\u77E5\uFF08notifications\uFF09\u548C\u4ED3\u5E93\uFF08repo\uFF0C\u67E5\u79C1\u6709\u4ED3\u5E93\u7684 CI\u3001PR \u8981\u7528\uFF1BGitHub \u6CA1\u6709\u53EA\u8BFB\u7684\u7248\u672C\uFF0C\u5C0F\u79D8\u4E66\u53EA\u8BFB\u4E0D\u5199\uFF09\u3002\u62FF\u5230\u7684 token \u53EA\u5B58\u5728\u8FD9\u53F0\u7535\u8111\u4E0A\u3002",
182
+ gDone: (login) => `\u767B\u5F55\u597D\u4E86\uFF0C\u8D26\u53F7 ${login}`,
183
+ gRetry: "\u518D\u8BD5\u4E00\u6B21",
184
+ gClose: "\u5173\u95ED",
162
185
  sTokenHow: "\u7528 classic token\uFF0C\u52FE\u9009 notifications\uFF1B\u8981\u770B\u79C1\u6709\u4ED3\u5E93\u518D\u52FE repo\u3002GitHub \u7684\u901A\u77E5\u63A5\u53E3\u4E0D\u8BA4 fine-grained token\u3002token \u53EA\u5B58\u8FDB\u7CFB\u7EDF\u94A5\u5319\u4E32\uFF0C\u4E0D\u5199\u8FDB\u4EFB\u4F55\u6587\u4EF6\u3002",
163
186
  sTokenNew: "\u53BB GitHub \u751F\u6210\uFF08\u6743\u9650\u5DF2\u52FE\u597D\uFF09",
164
187
  sTokenSave: "\u68C0\u67E5\u5E76\u4FDD\u5B58",
@@ -180,6 +203,7 @@ var zh = {
180
203
  sMail: "\u90AE\u7BB1",
181
204
  sMailPrivacy: "\u53EA\u8BFB\uFF1A\u5C0F\u79D8\u4E66\u7528\u53EA\u8BFB\u65B9\u5F0F\u6253\u5F00\u6536\u4EF6\u7BB1\uFF0C\u53EA\u770B\u53D1\u4EF6\u4EBA\u3001\u6536\u4EF6\u4EBA\u3001\u6807\u9898\u548C\u51E0\u4E2A\u90AE\u4EF6\u5934\uFF0C\u4E0D\u8BFB\u6B63\u6587\uFF0C\u4E5F\u4E0D\u4F1A\u628A\u90AE\u4EF6\u6807\u6210\u5DF2\u8BFB\u3002",
182
205
  sSecretsAt: (kind) => `\u5BC6\u7801\u4FDD\u5B58\u5728\uFF1A${kind}\uFF08\u52A0\u5BC6\u4FDD\u5B58\uFF0C\u4E0D\u5199\u8FDB\u4EFB\u4F55\u6587\u4EF6\uFF09`,
206
+ sSecretsFile: (path, why) => `\u8FD9\u53F0\u7535\u8111\u6CA1\u6709\u53EF\u7528\u7684\u7CFB\u7EDF\u94A5\u5319\u4E32\uFF08${why}\uFF09\uFF0C\u6240\u4EE5\u5BC6\u7801\u548C token \u5B58\u5728 ${path}\u3002\u8FD9\u4E2A\u6587\u4EF6\u53EA\u6709\u4F60\u7684\u7CFB\u7EDF\u8D26\u53F7\u80FD\u8BFB\uFF08\u6743\u9650 600\uFF09\uFF0C\u4F46\u6CA1\u6709\u52A0\u5BC6\uFF1A\u80FD\u767B\u5F55\u4F60\u8FD9\u4E2A\u8D26\u53F7\u7684\u4EBA\u90FD\u80FD\u770B\u5230\u3002`,
183
207
  sSecretsNone: (why) => `\u8FD9\u53F0\u7535\u8111\u4E0A\u6CA1\u6709\u53EF\u7528\u7684\u7CFB\u7EDF\u94A5\u5319\u4E32\uFF1A${why}\u3002\u5BC6\u7801\u4E0D\u4F1A\u88AB\u4FDD\u5B58\uFF1B\u8D26\u6237\u7167\u6837\u80FD\u4FDD\u5B58\uFF0C\u5BC6\u7801\u7528\u73AF\u5883\u53D8\u91CF\u63D0\u4F9B\uFF0C\u53D8\u91CF\u540D\u5199\u5728\u6BCF\u4E2A\u8D26\u6237\u4E0B\u9762\u3002`,
184
208
  sPasswordTestOnly: "\u6CA1\u6709\u94A5\u5319\u4E32\uFF1A\u8FD9\u91CC\u586B\u7684\u53EA\u7528\u6765\u6D4B\u8BD5\u8FDE\u63A5\uFF0C\u4E0D\u4F1A\u4FDD\u5B58",
185
209
  secretKinds: { macos: "macOS \u94A5\u5319\u4E32", windows: "Windows \u51ED\u636E\u7BA1\u7406\u5668", linux: "Linux \u5BC6\u94A5\u73AF\uFF08Secret Service\uFF09", memory: "\u5185\u5B58\uFF08\u6D4B\u8BD5\u7528\uFF09" },
@@ -203,8 +227,50 @@ var zh = {
203
227
  sMailOk: (n) => `\u8FDE\u4E0A\u4E86\uFF0C\u6700\u8FD1\u6709 ${n} \u5C01\u672A\u8BFB\u90AE\u4EF6`,
204
228
  sEnvHint: (name) => `\u6CA1\u6709\u627E\u5230\u5BC6\u7801\u3002\u70B9\u300C\u7F16\u8F91\u300D\u91CD\u65B0\u586B\u4E00\u6B21\uFF1B\u8FD9\u53F0\u7535\u8111\u6CA1\u6709\u94A5\u5319\u4E32\u7684\u8BDD\uFF0C\u5728\u542F\u52A8 DSH \u7684\u73AF\u5883\u91CC\u8BBE\u7F6E ${name}=\u6388\u6743\u7801\uFF0C\u518D\u91CD\u542F DSH\u3002`,
205
229
  sNeverChecked: "\u8FD8\u6CA1\u6536\u8FC7",
206
- sFeishu: "\u98DE\u4E66",
207
- sComing: "\u5373\u5C06\u63A5\u5165",
230
+ sNoPassword: "\u8FD8\u6CA1\u6709\u8FD9\u4E2A\u90AE\u7BB1\u7684\u5BC6\u7801\uFF08\u6388\u6743\u7801\uFF09\u3002\u70B9\u300C\u7F16\u8F91\u300D\u586B\u4E00\u6B21\u3002",
231
+ mTitleAdd: "\u6DFB\u52A0\u90AE\u7BB1",
232
+ mTitleEdit: "\u7F16\u8F91\u90AE\u7BB1",
233
+ mStepsHelp: "\u7167\u7740\u505A\u8FD8\u662F\u5361\u4F4F\u4E86\uFF1F",
234
+ mTestSave: "\u6D4B\u8BD5\u5E76\u4FDD\u5B58",
235
+ mSaveAnyway: "\u4E0D\u6D4B\u8BD5\uFF0C\u76F4\u63A5\u4FDD\u5B58",
236
+ mMore: "\u66F4\u591A\u8BBE\u7F6E",
237
+ mLength: (label, want, got) => `${label}${/[A-Za-z]$/.test(label) ? " " : ""}\u7684\u6388\u6743\u7801\uFF08\u5E94\u7528\u4E13\u7528\u5BC6\u7801\uFF09\u662F ${want} \u4F4D\uFF0C\u4F60\u586B\u7684\u662F ${got} \u4F4D\uFF0C\u53EF\u80FD\u586B\u6210\u4E86\u767B\u5F55\u5BC6\u7801\u3002`,
238
+ mSavedUnread: (n) => `\u5DF2\u4FDD\u5B58\u3002\u8FDE\u4E0A\u4E86\uFF0C\u6700\u8FD1\u6709 ${n} \u5C01\u672A\u8BFB\u90AE\u4EF6\u3002`,
239
+ bSources: "\u2460 \u5B83\u770B\u54EA\u91CC",
240
+ bSourcesHint: "\u5C0F\u79D8\u4E66\u4ECE\u8FD9\u4E9B\u5730\u65B9\u627E\u8981\u4F60\u5904\u7406\u7684\u4E8B\u3002",
241
+ bWhat: "\u2461 \u4EC0\u4E48\u4E8B\u8981\u544A\u8BC9\u4F60",
242
+ bUrgentRule: "\u8FD9\u4E9B\u7B97\u6025\u4E8B\uFF0C\u4E00\u51FA\u73B0\u5C31\u63D0\u9192\u4F60\uFF1A\u6709\u4EBA\u8BF7\u4F60 review\u3001@ \u4F60\u3001\u628A\u4E8B\u5206\u914D\u7ED9\u4F60\uFF1B\u4F1A\u8BAE\u9080\u8BF7\uFF1B\u4F60\u4EA4\u4EE3\u7684\u4E8B\u5230\u4E86\u5F53\u5929\u6216\u5DF2\u7ECF\u8FC7\u671F\u3002\u5176\u4ED6\u4E8B\u90FD\u653E\u8FDB\u6BCF\u5929\u7684\u7B80\u62A5\u3002",
243
+ bWhen: "\u2462 \u4EC0\u4E48\u65F6\u5019\u3001\u600E\u4E48\u544A\u8BC9\u4F60",
244
+ bDo: "\u2463 \u80FD\u66FF\u4F60\u529E\u4EC0\u4E48",
245
+ bMailReadOnly: "\u90AE\u4EF6\uFF1A\u53EA\u8BFB\u3002\u5C0F\u79D8\u4E66\u4ECE\u4E0D\u66FF\u4F60\u6539\u90AE\u7BB1\u91CC\u7684\u4EFB\u4F55\u4E1C\u897F\uFF0C\u4E5F\u4E0D\u4F1A\u628A\u90AE\u4EF6\u6807\u6210\u5DF2\u8BFB\u3002",
246
+ nBrief: "\u6BCF\u65E5\u7B80\u62A5\uFF0C\u65F6\u95F4",
247
+ nUrgent: "\u6025\u4E8B\u7ACB\u523B\u63D0\u9192",
248
+ nQuiet: "\u514D\u6253\u6270\uFF0C\u4ECE",
249
+ nTo: "\u5230",
250
+ nZone: (zone) => `\u6309 ${zone} \u7684\u65F6\u95F4\u3002\u514D\u6253\u6270\u671F\u95F4\u4E0D\u53D1\uFF0C\u671F\u95F4\u51FA\u73B0\u7684\u6025\u4E8B\u7B49\u7ED3\u675F\u540E\u4E00\u8D77\u53D1\u3002`,
251
+ nChannels: "\u53D1\u5230\u54EA\u91CC",
252
+ nDesktop: "\u7535\u8111\u901A\u77E5",
253
+ nDesktopOn: "\u5728\u8FD9\u53F0\u7535\u8111\u53F3\u4E0A\u89D2\u5F39\u51FA\u901A\u77E5\u3002",
254
+ nFeishu: "\u98DE\u4E66",
255
+ nFeishuOn: "\u53D1\u5230\u4F60\u7684\u98DE\u4E66\u7FA4\uFF0C\u624B\u673A\u4E0A\u4E5F\u80FD\u6536\u5230\u3002",
256
+ nTest: "\u53D1\u4E00\u6761\u8BD5\u8BD5",
257
+ nTestSent: "\u53D1\u51FA\u53BB\u4E86\uFF0C\u770B\u770B\u6536\u5230\u6CA1\u6709",
258
+ nFeishuConnect: "\u8FDE\u4E0A\u98DE\u4E66",
259
+ nFeishuChange: "\u6362\u4E00\u4E2A\u673A\u5668\u4EBA",
260
+ nFeishuRemove: "\u79FB\u9664",
261
+ nFeishuRemoveConfirm: "\u79FB\u9664\u98DE\u4E66\u673A\u5668\u4EBA\uFF1F\u4EE5\u540E\u63D0\u9192\u4E0D\u518D\u53D1\u5230\u98DE\u4E66\u3002",
262
+ nFeishuSaved: "\u98DE\u4E66\u8FDE\u4E0A\u4E86\uFF0C\u7FA4\u91CC\u5E94\u8BE5\u6536\u5230\u4E86\u4E00\u6761\u6D88\u606F\u3002",
263
+ nFeishuSteps: [
264
+ "\u5728\u98DE\u4E66\u91CC\u5EFA\u4E00\u4E2A\u7FA4\uFF0C\u53EA\u62C9\u4F60\u81EA\u5DF1\u4E5F\u884C\u3002",
265
+ '\u6253\u5F00\u7FA4\u8BBE\u7F6E \u2192\u300C\u7FA4\u673A\u5668\u4EBA\u300D\u2192\u300C\u6DFB\u52A0\u673A\u5668\u4EBA\u300D\u2192\u300C\u81EA\u5B9A\u4E49\u673A\u5668\u4EBA\u300D\uFF0C\u540D\u5B57\u586B"\u5C0F\u79D8\u4E66"\u3002',
266
+ '\u5B89\u5168\u8BBE\u7F6E\u9009\u300C\u7B7E\u540D\u6821\u9A8C\u300D\u5E76\u590D\u5236\u5BC6\u94A5\uFF1B\u6216\u8005\u9009\u300C\u81EA\u5B9A\u4E49\u5173\u952E\u8BCD\u300D\uFF0C\u5173\u952E\u8BCD\u586B"\u5C0F\u79D8\u4E66"\u3002',
267
+ "\u70B9\u300C\u6DFB\u52A0\u300D\uFF0C\u590D\u5236\u300CWebhook \u5730\u5740\u300D\uFF0C\u7C98\u8D34\u5230\u4E0B\u9762\u3002"
268
+ ],
269
+ nWebhook: "Webhook \u5730\u5740",
270
+ nSecret: "\u7B7E\u540D\u5BC6\u94A5\uFF08\u9009\u4E86\u7B7E\u540D\u6821\u9A8C\u624D\u586B\uFF09",
271
+ nFeishuPrivacy: "\u63D0\u9192\u7684\u5185\u5BB9\uFF08\u4E8B\u60C5\u7684\u6807\u9898\u548C\u7B80\u77ED\u8BF4\u660E\uFF09\u4F1A\u7ECF\u8FC7\u98DE\u4E66\u7684\u670D\u52A1\u5668\u3002\u5730\u5740\u548C\u5BC6\u94A5\u53EA\u5B58\u5728\u8FD9\u53F0\u7535\u8111\u4E0A\u3002",
272
+ nFeishuTry: "\u8FDE\u4E0A\u5E76\u53D1\u4E00\u6761\u8BD5\u8BD5",
273
+ nLast: (at, kind, results) => `\u4E0A\u6B21\u63D0\u9192\uFF1A${at} ${kind === "brief" ? "\u6BCF\u65E5\u7B80\u62A5" : kind === "urgent" ? "\u6025\u4E8B" : "\u6D4B\u8BD5"}\uFF0C${results.map((r) => `${r.channel === "feishu" ? "\u98DE\u4E66" : "\u7535\u8111\u901A\u77E5"}${r.ok ? "\u5DF2\u9001\u8FBE" : `\u6CA1\u9001\u5230\uFF08${r.error ?? ""}\uFF09`}`).join("\uFF0C")}`,
208
274
  ai: "+AI",
209
275
  aiTitle: "\u5728\u5BF9\u8BDD\u91CC\u95EE AI\uFF1A\u628A\u8FD9\u4EF6\u4E8B\u653E\u8FDB\u4E00\u4E2A\u65B0\u5BF9\u8BDD\u7684\u8F93\u5165\u6846\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u53D1\u9001\uFF0C\u4F60\u770B\u8FC7\u518D\u6309\u53D1\u9001",
210
276
  aiOpened: "\u5DF2\u653E\u8FDB\u65B0\u5BF9\u8BDD\u7684\u8F93\u5165\u6846\uFF0C\u770B\u8FC7\u540E\u6309\u53D1\u9001\u5C31\u884C",
@@ -270,6 +336,9 @@ var en = {
270
336
  restartTip: "After reinstalling or upgrading plugins, use this to restart DSH",
271
337
  restartConfirm: "Restart DSH? DSH needs a restart after you install or update plugins.",
272
338
  restarting: "Restarting\u2026 the window closes and opens again",
339
+ restartingWeb: "Restarting\u2026 the page loses its connection for a moment and reloads once DSH is back",
340
+ restartReloading: "DSH is back; reloading the page\u2026",
341
+ restartSlow: "Still not back after two minutes; reload the page yourself.",
273
342
  all: "All",
274
343
  sourceGithub: "GitHub",
275
344
  sourceMail: "Mail",
@@ -287,6 +356,19 @@ var en = {
287
356
  sTokenGh: "The account the GitHub CLI (gh) is signed in to; the token stays in the system keychain",
288
357
  sTokenKeychain: "The token you pasted here, kept in the system keychain",
289
358
  sTokenPaste: "Paste a GitHub token",
359
+ gLogin: "Sign in with GitHub",
360
+ gPasteInstead: "Or paste a token",
361
+ gStarting: "Asking GitHub for a sign-in code\u2026",
362
+ gStep1: "1. Copy this code",
363
+ gCopy: "Copy",
364
+ gCopied: "Copied",
365
+ gStep2: "2. Open GitHub, paste the code and click Authorize. A phone or another computer works too.",
366
+ gOpen: "Open GitHub",
367
+ gWaiting: "Waiting for you to authorize on GitHub\u2026",
368
+ gScopes: "It asks for two permissions: notifications, and repo (needed for CI runs and pull requests of private repositories; GitHub has no read-only version, and the secretary only reads). The token stays on this computer.",
369
+ gDone: (login) => `Signed in as ${login}`,
370
+ gRetry: "Try again",
371
+ gClose: "Close",
290
372
  sTokenHow: "Use a classic token with the notifications scope, plus repo for private repositories. GitHub's notifications API does not take fine-grained tokens. The token goes to the system keychain only, never to a file.",
291
373
  sTokenNew: "Create one on GitHub (scopes pre-ticked)",
292
374
  sTokenSave: "Check and save",
@@ -308,6 +390,7 @@ var en = {
308
390
  sMail: "Mail",
309
391
  sMailPrivacy: "Read-only: the secretary opens your inbox read-only and looks at sender, recipients, subject and a few headers only. It never reads bodies and never marks mail read.",
310
392
  sSecretsAt: (kind) => `Passwords are kept in: ${kind} (encrypted, never written to a file)`,
393
+ sSecretsFile: (path, why) => `No usable system keychain on this computer (${why}), so passwords and tokens are kept in ${path}. Only your system account can read that file (mode 600), but it is not encrypted: anyone who can sign in as you can read it.`,
311
394
  sSecretsNone: (why) => `No usable system keychain on this computer: ${why}. Passwords are not saved; you can still save accounts and give each password through the environment variable shown under it.`,
312
395
  sPasswordTestOnly: "No keychain: used only to test the connection, not saved",
313
396
  secretKinds: { macos: "the macOS Keychain", windows: "Windows Credential Manager", linux: "the Linux keyring (Secret Service)", memory: "memory (tests)" },
@@ -331,8 +414,50 @@ var en = {
331
414
  sMailOk: (n) => `Connected; ${n} unread messages recently`,
332
415
  sEnvHint: (name) => `No password found. Click Edit and enter it again; if this computer has no keychain, set ${name}=<app password> in the environment DSH starts from, then restart DSH.`,
333
416
  sNeverChecked: "Not checked yet",
334
- sFeishu: "Feishu / Lark",
335
- sComing: "Coming soon",
417
+ sNoPassword: "No password (app password) for this account yet. Click Edit and enter it.",
418
+ mTitleAdd: "Add a mail account",
419
+ mTitleEdit: "Edit mail account",
420
+ mStepsHelp: "Stuck on a step?",
421
+ mTestSave: "Test and save",
422
+ mSaveAnyway: "Save without testing",
423
+ mMore: "More settings",
424
+ mLength: (label, want, got) => `A ${label} app password is ${want} characters; this one is ${got}. It may be your sign-in password instead.`,
425
+ mSavedUnread: (n) => `Saved. Connected; ${n} unread messages recently.`,
426
+ bSources: "1. Where it looks",
427
+ bSourcesHint: "The secretary finds what needs you in these places.",
428
+ bWhat: "2. What to tell you about",
429
+ bUrgentRule: "These are urgent and reach you as they come: a review requested, a mention, an assignment; a meeting invitation; something you told it that is due today or overdue. Everything else goes into the daily brief.",
430
+ bWhen: "3. When and how to tell you",
431
+ bDo: "4. What it may do for you",
432
+ bMailReadOnly: "Mail: read-only. The secretary never changes anything in your mailbox and never marks mail read.",
433
+ nBrief: "Daily brief at",
434
+ nUrgent: "Urgent items right away",
435
+ nQuiet: "Quiet hours from",
436
+ nTo: "to",
437
+ nZone: (zone) => `In ${zone} time. Nothing is sent during quiet hours; urgent items from then come when they end.`,
438
+ nChannels: "Where to send",
439
+ nDesktop: "This computer",
440
+ nDesktopOn: "A notification in the corner of this screen.",
441
+ nFeishu: "Feishu / Lark",
442
+ nFeishuOn: "To your Feishu group, on your phone too.",
443
+ nTest: "Send a test",
444
+ nTestSent: "Sent; check that it arrived",
445
+ nFeishuConnect: "Connect Feishu",
446
+ nFeishuChange: "Use another bot",
447
+ nFeishuRemove: "Remove",
448
+ nFeishuRemoveConfirm: "Remove the Feishu bot? Reminders will no longer go to Feishu.",
449
+ nFeishuSaved: "Feishu is connected; the group should have a message.",
450
+ nFeishuSteps: [
451
+ "Create a Feishu group; just you is fine.",
452
+ 'Group settings \u2192 Bots \u2192 Add bot \u2192 Custom bot; name it "\u5C0F\u79D8\u4E66".',
453
+ 'Under security choose signature verification and copy the secret, or a custom keyword set to "\u5C0F\u79D8\u4E66".',
454
+ "Click Add, copy the webhook address and paste it below."
455
+ ],
456
+ nWebhook: "Webhook address",
457
+ nSecret: "Signing secret (only with signature verification)",
458
+ nFeishuPrivacy: "What reminders say (titles and a short note) passes through Feishu's servers. The address and secret stay on this computer.",
459
+ nFeishuTry: "Connect and send a test",
460
+ nLast: (at, kind, results) => `Last reminder: ${at}, ${kind === "brief" ? "daily brief" : kind === "urgent" ? "urgent" : "test"}; ${results.map((r) => `${r.channel === "feishu" ? "Feishu" : "this computer"} ${r.ok ? "delivered" : `failed (${r.error ?? ""})`}`).join(", ")}`,
336
461
  ai: "+AI",
337
462
  aiTitle: "Ask AI in a chat: puts this into a new chat's input box, unsent; read it, then press send",
338
463
  aiOpened: "Put into a new chat's input box; read it and press send",
@@ -391,19 +516,190 @@ function AskAI({ topic, lang, locale, openChat: openChat2, compact = false }) {
391
516
  }
392
517
 
393
518
  // src/client/Settings.tsx
519
+ var import_react4 = require("react");
520
+
521
+ // src/client/Modal.tsx
394
522
  var import_react2 = require("react");
523
+ var import_react_dom = require("react-dom");
395
524
  var import_jsx_runtime2 = require("react/jsx-runtime");
525
+ function Modal({ title, onClose, children }) {
526
+ (0, import_react2.useEffect)(() => {
527
+ const key = (event) => {
528
+ if (event.key === "Escape") onClose();
529
+ };
530
+ window.addEventListener("keydown", key);
531
+ return () => window.removeEventListener("keydown", key);
532
+ }, [onClose]);
533
+ return (0, import_react_dom.createPortal)(
534
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-modal-backdrop", onMouseDown: (event) => {
535
+ if (event.target === event.currentTarget) onClose();
536
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-modal", role: "dialog", "aria-modal": "true", "aria-label": title, children: [
537
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-modal-head", children: [
538
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: title }),
539
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", "aria-label": "\xD7", onClick: onClose, children: "\xD7" })
540
+ ] }),
541
+ children
542
+ ] }) }),
543
+ document.body
544
+ );
545
+ }
546
+
547
+ // src/client/Notify.tsx
548
+ var import_react3 = require("react");
549
+ var import_jsx_runtime3 = require("react/jsx-runtime");
550
+ var browserZone = () => {
551
+ try {
552
+ return Intl.DateTimeFormat().resolvedOptions().timeZone ?? "";
553
+ } catch {
554
+ return "";
555
+ }
556
+ };
557
+ function NotifySection({ view, lang, locale, take }) {
558
+ const t = TEXT[lang];
559
+ const notify = view.notify.settings;
560
+ const [tested, setTested] = (0, import_react3.useState)({});
561
+ const [feishuOpen, setFeishuOpen] = (0, import_react3.useState)(false);
562
+ const save = async (patch) => take(await settingsApi.saveNotify(locale(), patch), t.sSaved);
563
+ (0, import_react3.useEffect)(() => {
564
+ const zone = browserZone();
565
+ if (notify.timeZone === "" && zone !== "") void settingsApi.saveNotify(locale(), { timeZone: zone }).then((r) => take(r));
566
+ }, [notify.timeZone, locale]);
567
+ const test = async (channel) => {
568
+ setTested({ ...tested, [channel]: { ok: true, text: t.sTesting } });
569
+ const r = await settingsApi.testNotify(locale(), channel);
570
+ setTested({ ...tested, [channel]: r.ok ? { ok: true, text: t.nTestSent } : { ok: false, text: r.message } });
571
+ };
572
+ const time = (value, onChange) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "time", step: 60, value, onChange: (e) => {
573
+ if (e.target.value !== "") onChange(e.target.value);
574
+ } });
575
+ const last = view.notify.last;
576
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
577
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-notify-row", children: [
578
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-check", children: [
579
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "checkbox", checked: notify.brief.enabled, onChange: (e) => {
580
+ void save({ brief: { ...notify.brief, enabled: e.target.checked } });
581
+ } }),
582
+ t.nBrief
583
+ ] }),
584
+ time(notify.brief.time, (value) => {
585
+ void save({ brief: { ...notify.brief, time: value } });
586
+ })
587
+ ] }),
588
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-notify-row", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-check", children: [
589
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "checkbox", checked: notify.urgent, onChange: (e) => {
590
+ void save({ urgent: e.target.checked });
591
+ } }),
592
+ t.nUrgent
593
+ ] }) }),
594
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-notify-row", children: [
595
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-check", children: [
596
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "checkbox", checked: notify.quiet.enabled, onChange: (e) => {
597
+ void save({ quiet: { ...notify.quiet, enabled: e.target.checked } });
598
+ } }),
599
+ t.nQuiet
600
+ ] }),
601
+ time(notify.quiet.from, (value) => {
602
+ void save({ quiet: { ...notify.quiet, from: value } });
603
+ }),
604
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mishu-hint", children: t.nTo }),
605
+ time(notify.quiet.to, (value) => {
606
+ void save({ quiet: { ...notify.quiet, to: value } });
607
+ })
608
+ ] }),
609
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mishu-hint", children: t.nZone(notify.timeZone || browserZone()) }),
610
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t.nChannels }),
611
+ view.notify.channels.map((channel) => {
612
+ const result = tested[channel.id];
613
+ const feishu = channel.id === "feishu";
614
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-card", children: [
615
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-notify-row", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-check", children: [
616
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
617
+ "input",
618
+ {
619
+ type: "checkbox",
620
+ checked: channel.enabled,
621
+ disabled: !channel.available,
622
+ onChange: (e) => {
623
+ void save({ channels: { ...notify.channels, [channel.id]: e.target.checked } });
624
+ }
625
+ }
626
+ ),
627
+ feishu ? t.nFeishu : t.nDesktop
628
+ ] }) }),
629
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `mishu-card-note ${channel.available ? "" : "mishu-warn"}`, children: channel.available ? feishu ? t.nFeishuOn : t.nDesktopOn : channel.why }),
630
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-actions", children: [
631
+ channel.available ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => {
632
+ void test(channel.id);
633
+ }, children: t.nTest }) : null,
634
+ feishu ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: view.notify.feishu.configured ? "mishu-btn" : "mishu-btn primary", onClick: () => setFeishuOpen(true), children: view.notify.feishu.configured ? t.nFeishuChange : t.nFeishuConnect }) : null,
635
+ feishu && view.notify.feishu.configured ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", onClick: () => {
636
+ if (window.confirm(t.nFeishuRemoveConfirm)) void settingsApi.deleteFeishu(locale()).then((r) => take(r));
637
+ }, children: t.nFeishuRemove }) : null,
638
+ result === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: result.ok ? "mishu-hint" : "mishu-hint mishu-danger", children: result.text })
639
+ ] })
640
+ ] }, channel.id);
641
+ }),
642
+ last === null ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mishu-hint", children: t.nLast(new Date(last.at).toLocaleString(), last.kind, last.results) }),
643
+ feishuOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FeishuDialog, { lang, locale, onDone: (next) => {
644
+ setFeishuOpen(false);
645
+ if (next !== void 0) take({ ok: true, view: next }, t.nFeishuSaved);
646
+ } }) : null
647
+ ] });
648
+ }
649
+ function FeishuDialog({ lang, locale, onDone }) {
650
+ const t = TEXT[lang];
651
+ const [webhook, setWebhook] = (0, import_react3.useState)("");
652
+ const [secret, setSecret] = (0, import_react3.useState)("");
653
+ const [busy, setBusy] = (0, import_react3.useState)(false);
654
+ const [error, setError] = (0, import_react3.useState)();
655
+ const connect = async () => {
656
+ setBusy(true);
657
+ setError(void 0);
658
+ const r = await settingsApi.saveFeishu(locale(), webhook, secret);
659
+ setBusy(false);
660
+ if (r.ok) onDone(r.view);
661
+ else setError(r.message);
662
+ };
663
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Modal, { title: t.nFeishuConnect, onClose: () => onDone(), children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-steps", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ol", { children: t.nFeishuSteps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: step }, i)) }) }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
666
+ t.nWebhook,
667
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: webhook, autoComplete: "off", spellCheck: false, placeholder: "https://open.feishu.cn/open-apis/bot/v2/hook/\u2026", onChange: (e) => {
668
+ setWebhook(e.target.value);
669
+ setError(void 0);
670
+ } })
671
+ ] }),
672
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
673
+ t.nSecret,
674
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "password", value: secret, autoComplete: "off", spellCheck: false, onChange: (e) => {
675
+ setSecret(e.target.value);
676
+ setError(void 0);
677
+ } })
678
+ ] }),
679
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mishu-hint", children: t.nFeishuPrivacy }),
680
+ error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-preview mishu-danger", children: error }),
681
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-actions", children: [
682
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn primary", disabled: busy || webhook.trim() === "", onClick: () => {
683
+ void connect();
684
+ }, children: busy ? t.sTesting : t.nFeishuTry }),
685
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => onDone(), children: t.sCancel })
686
+ ] })
687
+ ] });
688
+ }
689
+
690
+ // src/client/Settings.tsx
691
+ var import_jsx_runtime4 = require("react/jsx-runtime");
396
692
  var CATEGORIES = ["document", "bill", "birthday", "renewal", "other"];
397
693
  function MishuSettings({ lang, locale, openChat: openChat2 }) {
398
694
  const t = TEXT[lang];
399
- const [view, setView] = (0, import_react2.useState)();
400
- const [error, setError] = (0, import_react2.useState)();
401
- const [note, setNote] = (0, import_react2.useState)();
402
- const [lead, setLead] = (0, import_react2.useState)({});
403
- const [githubTest, setGithubTest] = (0, import_react2.useState)();
404
- const [confirmOn, setConfirmOn] = (0, import_react2.useState)(false);
405
- const [pasting, setPasting] = (0, import_react2.useState)(false);
406
- const [editing, setEditing] = (0, import_react2.useState)();
695
+ const [view, setView] = (0, import_react4.useState)();
696
+ const [error, setError] = (0, import_react4.useState)();
697
+ const [note, setNote] = (0, import_react4.useState)();
698
+ const [lead, setLead] = (0, import_react4.useState)({});
699
+ const [githubTest, setGithubTest] = (0, import_react4.useState)();
700
+ const [confirmOn, setConfirmOn] = (0, import_react4.useState)(false);
701
+ const [pasting, setPasting] = (0, import_react4.useState)(false);
702
+ const [editing, setEditing] = (0, import_react4.useState)();
407
703
  const take = (result, saved) => {
408
704
  if (result.ok) {
409
705
  setView(result.view);
@@ -414,11 +710,11 @@ function MishuSettings({ lang, locale, openChat: openChat2 }) {
414
710
  setError(result.message);
415
711
  }
416
712
  };
417
- (0, import_react2.useEffect)(() => {
713
+ (0, import_react4.useEffect)(() => {
418
714
  void settingsApi.load(locale()).then((r) => take(r));
419
715
  }, [locale]);
420
716
  if (view === void 0) {
421
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-settings", children: error === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-empty", children: t.loading }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-error", children: [
717
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-settings", children: error === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-empty", children: t.loading }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-error", children: [
422
718
  t.failed,
423
719
  error
424
720
  ] }) });
@@ -426,95 +722,76 @@ function MishuSettings({ lang, locale, openChat: openChat2 }) {
426
722
  const save = async (settings) => take(await settingsApi.save(locale(), settings), t.sSaved);
427
723
  const token = view.github.token;
428
724
  const secretKind = t.secretKinds[view.secrets.kind] ?? view.secrets.kind;
429
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-settings", children: [
430
- error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-error", children: [
725
+ const markRead = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
726
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
727
+ t.sMarkRead,
728
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("select", { value: view.settings.markRead, onChange: (e) => {
729
+ const value = e.target.value;
730
+ if (value === "on") setConfirmOn(true);
731
+ else void save({ markRead: value });
732
+ }, children: [
733
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "off", children: t.sMarkOff }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "shadow", children: t.sMarkShadow }),
735
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "on", children: t.sMarkOn })
736
+ ] })
737
+ ] }),
738
+ confirmOn ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-banner", children: [
739
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: t.sMarkConfirm }),
740
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-actions", children: [
741
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: () => {
742
+ setConfirmOn(false);
743
+ void save({ markRead: "on" });
744
+ }, children: t.sConfirm }),
745
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => setConfirmOn(false), children: t.sCancel })
746
+ ] })
747
+ ] }) : null
748
+ ] });
749
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-settings", children: [
750
+ error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-error", children: [
431
751
  t.failed,
432
752
  error
433
753
  ] }),
434
- note === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-ok", children: note }),
435
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "mishu-panel", children: [
436
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t.sGeneral }),
437
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
438
- t.sLanguage,
439
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: view.settings.language, onChange: (e) => {
440
- void save({ language: e.target.value });
441
- }, children: [
442
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "auto", children: t.sLanguageAuto }),
443
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "zh", children: "\u4E2D\u6587" }),
444
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "en", children: "English" })
445
- ] })
446
- ] }),
447
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-field", children: t.sLead }),
448
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-grid5", children: CATEGORIES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
449
- t.categories[c],
450
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "number", min: 0, max: 366, value: lead[c] ?? 0, onChange: (e) => setLead({ ...lead, [c]: Number(e.target.value) }) })
451
- ] }, c)) }),
452
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: () => {
453
- void save({ leadDays: lead });
454
- }, children: t.sSave })
455
- ] }),
456
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "mishu-panel", children: [
457
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t.sGithub }),
458
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-check", children: [
459
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: view.settings.githubEnabled, onChange: (e) => {
754
+ note === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-ok", children: note }),
755
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "mishu-panel", children: [
756
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { children: t.bSources }),
757
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint", children: t.bSourcesHint }),
758
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: t.sGithub }),
759
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-check", children: [
760
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", checked: view.settings.githubEnabled, onChange: (e) => {
460
761
  void save({ githubEnabled: e.target.checked });
461
762
  } }),
462
763
  t.sGithubOn
463
764
  ] }),
464
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-row-explain", children: [
465
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "mishu-row-label", children: t.sToken }),
466
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: token.found ? "" : "mishu-danger", children: token.found ? token.from === "env" ? t.sTokenEnv(view.github.tokenEnv) : token.from === "keychain" ? t.sTokenKeychain : t.sTokenGh : token.text })
765
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-row-explain", children: [
766
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "mishu-row-label", children: t.sToken }),
767
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: token.found ? "" : "mishu-danger", children: token.found ? token.from === "env" ? t.sTokenEnv(view.github.tokenEnv) : token.from === "keychain" ? t.sTokenKeychain : t.sTokenGh : token.text })
467
768
  ] }),
468
- token.found ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-actions", children: [
469
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => {
769
+ token.found ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-actions", children: [
770
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => {
470
771
  setGithubTest(t.sTesting);
471
772
  void settingsApi.testGithub(locale()).then((r) => setGithubTest(r.ok ? t.sGithubOk(r.login) : `${t.failed}${r.message}`));
472
773
  }, children: t.sTest }),
473
- token.from === "env" || pasting ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => setPasting(true), children: t.sTokenReplace }),
474
- token.from === "keychain" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", onClick: () => {
774
+ token.from === "env" || pasting ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => setPasting(true), children: t.sTokenReplace }),
775
+ token.from === "keychain" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", onClick: () => {
475
776
  if (window.confirm(t.sTokenRemoveConfirm)) void settingsApi.deleteToken(locale()).then((r) => {
476
777
  setGithubTest(void 0);
477
778
  take(r);
478
779
  });
479
780
  }, children: t.sTokenRemove }) : null,
480
- githubTest === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "mishu-hint", children: githubTest })
781
+ githubTest === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "mishu-hint", children: githubTest })
481
782
  ] }) : null,
482
- !token.found || pasting ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TokenForm, { view, lang, locale, onSaved: (next, login) => {
783
+ !token.found || pasting ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(GithubConnect, { view, lang, locale, onSaved: (next, login) => {
483
784
  setPasting(false);
484
785
  setGithubTest(t.sTokenSaved(login));
485
786
  take({ ok: true, view: next });
486
787
  }, onCancel: token.found ? () => setPasting(false) : void 0 }) : null,
487
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
488
- t.sMarkRead,
489
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: view.settings.markRead, onChange: (e) => {
490
- const value = e.target.value;
491
- if (value === "on") setConfirmOn(true);
492
- else void save({ markRead: value });
493
- }, children: [
494
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "off", children: t.sMarkOff }),
495
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "shadow", children: t.sMarkShadow }),
496
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "on", children: t.sMarkOn })
497
- ] })
498
- ] }),
499
- confirmOn ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-banner", children: [
500
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: t.sMarkConfirm }),
501
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-actions", children: [
502
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: () => {
503
- setConfirmOn(false);
504
- void save({ markRead: "on" });
505
- }, children: t.sConfirm }),
506
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => setConfirmOn(false), children: t.sCancel })
507
- ] })
508
- ] }) : null
509
- ] }),
510
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "mishu-panel", children: [
511
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t.sMail }),
512
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-hint", children: t.sMailPrivacy }),
513
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: view.secrets.available ? "mishu-hint" : "mishu-hint mishu-warn", children: view.secrets.available ? t.sSecretsAt(secretKind) : t.sSecretsNone(view.secrets.why ?? secretKind) }),
514
- view.accounts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-empty", children: t.sNoAccounts }) : view.accounts.map((account) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
788
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: t.sMail }),
789
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint", children: t.sMailPrivacy }),
790
+ view.accounts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-empty", children: t.sNoAccounts }) : view.accounts.map((account) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
515
791
  AccountRow,
516
792
  {
517
793
  account,
794
+ secretsOk: view.secrets.available,
518
795
  lang,
519
796
  locale,
520
797
  openChat: openChat2,
@@ -523,24 +800,164 @@ function MishuSettings({ lang, locale, openChat: openChat2 }) {
523
800
  },
524
801
  account.id
525
802
  )),
526
- editing === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn mishu-add-toggle", onClick: () => setEditing({ preset: "qq", sinceDays: 14, enabled: true }), children: t.sAddAccount }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AccountForm, { draft: editing, view, lang, locale, openChat: openChat2, onDone: (next) => {
527
- setEditing(void 0);
528
- if (next !== void 0) take({ ok: true, view: next }, t.sSaved);
529
- } })
803
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn mishu-add-toggle", onClick: () => setEditing({ preset: "custom", sinceDays: 14, enabled: true }), children: t.sAddAccount }),
804
+ editing === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
805
+ MailDialog,
806
+ {
807
+ draft: editing,
808
+ view,
809
+ lang,
810
+ locale,
811
+ openChat: openChat2,
812
+ onDone: (next, note2) => {
813
+ setEditing(void 0);
814
+ if (next !== void 0) take({ ok: true, view: next }, note2 ?? t.sSaved);
815
+ }
816
+ }
817
+ )
818
+ ] }),
819
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "mishu-panel", children: [
820
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { children: t.bWhat }),
821
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint", children: t.bUrgentRule }),
822
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-field", children: t.sLead }),
823
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-grid5", children: CATEGORIES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
824
+ t.categories[c],
825
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "number", min: 0, max: 366, value: lead[c] ?? 0, onChange: (e) => setLead({ ...lead, [c]: Number(e.target.value) }) })
826
+ ] }, c)) }),
827
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: () => {
828
+ void save({ leadDays: lead });
829
+ }, children: t.sSave })
830
+ ] }),
831
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "mishu-panel", children: [
832
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { children: t.bWhen }),
833
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NotifySection, { view, lang, locale, take })
530
834
  ] }),
531
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "mishu-panel", children: [
532
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t.sFeishu }),
533
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-hint", children: t.sComing })
835
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "mishu-panel", children: [
836
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { children: t.bDo }),
837
+ markRead,
838
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint", children: t.bMailReadOnly })
839
+ ] }),
840
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "mishu-panel", children: [
841
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { children: t.sGeneral }),
842
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
843
+ t.sLanguage,
844
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("select", { value: view.settings.language, onChange: (e) => {
845
+ void save({ language: e.target.value });
846
+ }, children: [
847
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "auto", children: t.sLanguageAuto }),
848
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "zh", children: "\u4E2D\u6587" }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "en", children: "English" })
850
+ ] })
851
+ ] }),
852
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: view.secrets.available && view.secrets.file === void 0 ? "mishu-hint" : "mishu-hint mishu-warn", children: !view.secrets.available ? t.sSecretsNone(view.secrets.why ?? secretKind) : view.secrets.file !== void 0 ? t.sSecretsFile(view.secrets.file.path, view.secrets.file.why) : t.sSecretsAt(secretKind) })
534
853
  ] })
535
854
  ] });
536
855
  }
856
+ function GithubConnect({ view, lang, locale, onSaved, onCancel }) {
857
+ const t = TEXT[lang];
858
+ const [signingIn, setSigningIn] = (0, import_react4.useState)(false);
859
+ const [paste, setPaste] = (0, import_react4.useState)(!view.github.deviceLogin);
860
+ if (!view.github.deviceLogin) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TokenForm, { view, lang, locale, onSaved, onCancel });
861
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
862
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-actions", children: [
863
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: () => setSigningIn(true), children: t.gLogin }),
864
+ paste ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", onClick: () => setPaste(true), children: t.gPasteInstead }),
865
+ onCancel === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: onCancel, children: t.sCancel })
866
+ ] }),
867
+ paste ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TokenForm, { view, lang, locale, onSaved, onCancel: void 0 }) : null,
868
+ signingIn ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(GithubLoginDialog, { lang, locale, onClose: () => setSigningIn(false), onSaved: (next, login) => {
869
+ setSigningIn(false);
870
+ onSaved(next, login);
871
+ } }) : null
872
+ ] });
873
+ }
874
+ function GithubLoginDialog({ lang, locale, onClose, onSaved }) {
875
+ const t = TEXT[lang];
876
+ const [login, setLogin] = (0, import_react4.useState)();
877
+ const [error, setError] = (0, import_react4.useState)();
878
+ const [copied, setCopied] = (0, import_react4.useState)(false);
879
+ const [attempt, setAttempt] = (0, import_react4.useState)(0);
880
+ const saved = (0, import_react4.useRef)(void 0);
881
+ (0, import_react4.useEffect)(() => {
882
+ let alive = true;
883
+ let timer;
884
+ setLogin(void 0);
885
+ setError(void 0);
886
+ const poll = async () => {
887
+ const r = await settingsApi.deviceStatus(locale());
888
+ if (!alive) return;
889
+ if (!r.ok) {
890
+ setError(r.message);
891
+ return;
892
+ }
893
+ setLogin(r.login);
894
+ if (r.login.state === "done" && r.view !== void 0) saved.current = { view: r.view, login: r.login.login };
895
+ else if (r.login.state === "waiting") timer = setTimeout(() => {
896
+ void poll();
897
+ }, 2e3);
898
+ };
899
+ void settingsApi.deviceStart(locale()).then((r) => {
900
+ if (!alive) return;
901
+ if (!r.ok) {
902
+ setError(r.message);
903
+ return;
904
+ }
905
+ setLogin(r.login);
906
+ timer = setTimeout(() => {
907
+ void poll();
908
+ }, 2e3);
909
+ });
910
+ return () => {
911
+ alive = false;
912
+ if (timer !== void 0) clearTimeout(timer);
913
+ };
914
+ }, [attempt, locale]);
915
+ const close = () => {
916
+ if (saved.current !== void 0) onSaved(saved.current.view, saved.current.login);
917
+ else {
918
+ void settingsApi.deviceCancel(locale());
919
+ onClose();
920
+ }
921
+ };
922
+ const copy2 = (code) => {
923
+ void navigator.clipboard?.writeText(code).then(() => setCopied(true), () => setCopied(false));
924
+ };
925
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Modal, { title: t.gLogin, onClose: close, children: error !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-preview mishu-danger", children: error }),
927
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-actions", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: close, children: t.gClose }) })
928
+ ] }) : login === void 0 || login.state === "idle" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint", children: t.gStarting }) : login.state === "waiting" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
929
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-field", children: t.gStep1 }),
930
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-code-row", children: [
931
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { className: "mishu-code", children: login.userCode }),
932
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => copy2(login.userCode), children: copied ? t.gCopied : t.gCopy })
933
+ ] }),
934
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-field", children: t.gStep2 }),
935
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-actions", children: [
936
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: () => window.open(login.verificationUri, "_blank", "noopener"), children: t.gOpen }),
937
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "mishu-hint", children: t.gWaiting })
938
+ ] }),
939
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint", children: t.gScopes })
940
+ ] }) : login.state === "done" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
941
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-ok", children: t.gDone(login.login) }),
942
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-actions", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: close, children: t.gClose }) })
943
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
944
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-preview mishu-danger", children: login.message }),
945
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-actions", children: [
946
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: () => {
947
+ setCopied(false);
948
+ setAttempt(attempt + 1);
949
+ }, children: t.gRetry }),
950
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: close, children: t.gClose })
951
+ ] })
952
+ ] }) });
953
+ }
537
954
  function TokenForm({ view, lang, locale, onSaved, onCancel }) {
538
955
  const t = TEXT[lang];
539
- const [token, setToken] = (0, import_react2.useState)("");
540
- const [busy, setBusy] = (0, import_react2.useState)(false);
541
- const [message, setMessage] = (0, import_react2.useState)();
956
+ const [token, setToken] = (0, import_react4.useState)("");
957
+ const [busy, setBusy] = (0, import_react4.useState)(false);
958
+ const [message, setMessage] = (0, import_react4.useState)();
542
959
  if (!view.secrets.available) {
543
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-hint mishu-warn", children: t.sTokenNoKeychain(view.github.tokenEnv, view.secrets.why ?? "") });
960
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint mishu-warn", children: t.sTokenNoKeychain(view.github.tokenEnv, view.secrets.why ?? "") });
544
961
  }
545
962
  const save = async () => {
546
963
  setBusy(true);
@@ -554,162 +971,191 @@ function TokenForm({ view, lang, locale, onSaved, onCancel }) {
554
971
  setMessage(r.message);
555
972
  }
556
973
  };
557
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-add", children: [
558
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
974
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-add", children: [
975
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
559
976
  t.sTokenPaste,
560
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "password", value: token, autoComplete: "off", spellCheck: false, placeholder: "ghp_\u2026", onChange: (e) => {
977
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "password", value: token, autoComplete: "off", spellCheck: false, placeholder: "ghp_\u2026", onChange: (e) => {
561
978
  setToken(e.target.value);
562
979
  setMessage(void 0);
563
980
  } })
564
981
  ] }),
565
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-hint", children: t.sTokenHow }),
566
- message === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-preview mishu-danger", children: message }),
567
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-actions", children: [
568
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => window.open(view.github.newTokenUrl, "_blank", "noopener"), children: t.sTokenNew }),
569
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn primary", disabled: busy || token.trim() === "", onClick: () => {
982
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint", children: t.sTokenHow }),
983
+ message === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-preview mishu-danger", children: message }),
984
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-actions", children: [
985
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => window.open(view.github.newTokenUrl, "_blank", "noopener"), children: t.sTokenNew }),
986
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn primary", disabled: busy || token.trim() === "", onClick: () => {
570
987
  void save();
571
988
  }, children: busy ? t.sTesting : t.sTokenSave }),
572
- onCancel === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: onCancel, children: t.sCancel })
989
+ onCancel === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: onCancel, children: t.sCancel })
573
990
  ] })
574
991
  ] });
575
992
  }
576
- function AccountRow({ account, lang, locale, openChat: openChat2, onEdit, onView }) {
993
+ function AccountRow({ account, secretsOk, lang, locale, openChat: openChat2, onEdit, onView }) {
577
994
  const t = TEXT[lang];
578
- const [busy, setBusy] = (0, import_react2.useState)(false);
995
+ const [busy, setBusy] = (0, import_react4.useState)(false);
579
996
  const report = account.report;
580
- const status = report === null ? t.sNeverChecked : report.error !== void 0 ? report.error[lang] : lang === "zh" ? `${new Date(report.at).toLocaleString()} \u6536\u4FE1\uFF0C${report.messages} \u5C01\u672A\u8BFB` : `checked ${new Date(report.at).toLocaleString()}, ${report.messages} unread`;
581
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-card", children: [
582
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-card-title", children: [
997
+ const status = !account.hasPassword ? secretsOk ? t.sNoPassword : t.sEnvHint(account.envName) : report === null ? t.sNeverChecked : report.error !== void 0 ? report.error[lang] : lang === "zh" ? `${new Date(report.at).toLocaleString()} \u6536\u4FE1\uFF0C${report.messages} \u5C01\u672A\u8BFB` : `checked ${new Date(report.at).toLocaleString()}, ${report.messages} unread`;
998
+ const failed = !account.hasPassword || report?.error !== void 0;
999
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-card", children: [
1000
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-card-title", children: [
583
1001
  account.label,
584
1002
  account.enabled ? "" : ` (${lang === "zh" ? "\u5DF2\u505C\u7528" : "disabled"})`
585
1003
  ] }),
586
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-card-meta", children: [
1004
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-card-meta", children: [
587
1005
  account.address,
588
1006
  " \xB7 ",
589
1007
  account.host,
590
1008
  ":",
591
1009
  account.port
592
1010
  ] }),
593
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `mishu-card-note ${report?.error === void 0 ? "" : "mishu-danger"}`, children: [
1011
+ account.mismatch === null ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mishu-card-note mishu-warn", children: account.mismatch }),
1012
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `mishu-card-note ${failed ? "mishu-danger" : ""}`, children: [
594
1013
  status,
595
- report?.error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AskAI, { topic: { kind: "mail-error", accountId: account.id }, lang, locale, openChat: openChat2, compact: true })
1014
+ account.hasPassword && report?.error !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AskAI, { topic: { kind: "mail-error", accountId: account.id }, lang, locale, openChat: openChat2, compact: true }) : null
596
1015
  ] }),
597
- account.hasPassword ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-card-note mishu-warn", children: t.sEnvHint(account.envName) }),
598
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-actions", children: [
599
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", disabled: busy, onClick: () => {
1016
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-actions", children: [
1017
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", disabled: busy || !account.hasPassword, onClick: () => {
600
1018
  setBusy(true);
601
1019
  void settingsApi.syncMail(locale(), account.id).then((r) => {
602
1020
  setBusy(false);
603
1021
  onView(r);
604
1022
  });
605
1023
  }, children: busy ? t.sTesting : t.sCheckNow }),
606
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: onEdit, children: t.sEdit }),
607
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", onClick: () => {
1024
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: account.hasPassword && account.mismatch === null ? "mishu-btn" : "mishu-btn primary", onClick: onEdit, children: t.sEdit }),
1025
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", onClick: () => {
608
1026
  if (window.confirm(t.sDeleteConfirm)) void settingsApi.deleteMail(locale(), account.id).then(onView);
609
1027
  }, children: t.sDelete })
610
1028
  ] })
611
1029
  ] });
612
1030
  }
613
- function AccountForm({ draft, view, lang, locale, openChat: openChat2, onDone }) {
1031
+ function MailDialog({ draft, view, lang, locale, openChat: openChat2, onDone }) {
614
1032
  const t = TEXT[lang];
615
- const [account, setAccount] = (0, import_react2.useState)(draft);
616
- const [password, setPassword] = (0, import_react2.useState)("");
617
- const [message, setMessage] = (0, import_react2.useState)();
618
- const [busy, setBusy] = (0, import_react2.useState)(false);
1033
+ const existing = draft.id !== void 0;
1034
+ const [account, setAccount] = (0, import_react4.useState)(draft);
1035
+ const [picked, setPicked] = (0, import_react4.useState)(existing);
1036
+ const [password, setPassword] = (0, import_react4.useState)("");
1037
+ const [message, setMessage] = (0, import_react4.useState)();
1038
+ const [failedTest, setFailedTest] = (0, import_react4.useState)(false);
1039
+ const [busy, setBusy] = (0, import_react4.useState)(false);
619
1040
  const preset = view.presets.find((p) => p.id === account.preset) ?? view.presets[view.presets.length - 1];
620
1041
  const custom = preset.id === "custom";
621
- const existing = draft.id !== void 0;
622
1042
  const set = (patch) => {
623
1043
  setAccount({ ...account, ...patch });
624
1044
  setMessage(void 0);
1045
+ setFailedTest(false);
625
1046
  };
626
- const test = async () => {
627
- setBusy(true);
628
- setMessage({ ok: true, text: t.sTesting });
629
- const r = await settingsApi.testMail(locale(), account, password);
630
- setBusy(false);
631
- setMessage(r.ok ? { ok: true, text: t.sMailOk(r.unread) } : { ok: false, text: r.message });
1047
+ const setAddress = (address) => {
1048
+ const domain = address.trim().toLowerCase().split("@")[1] ?? "";
1049
+ const known = view.presets.find((p) => p.domains.includes(domain));
1050
+ set({ address, ...picked ? {} : known !== void 0 ? { preset: known.id } : domain.includes(".") ? { preset: "custom" } : {} });
632
1051
  };
633
- const save = async () => {
634
- setBusy(true);
1052
+ const typed = password.replace(/\s+/g, "");
1053
+ const lengthOff = preset.passwordLength !== null && typed !== "" && typed.length !== preset.passwordLength;
1054
+ const save = async (unread) => {
635
1055
  const r = await settingsApi.saveMail(locale(), account, password);
636
1056
  setBusy(false);
1057
+ if (r.ok) onDone(r.view, unread === void 0 ? void 0 : t.mSavedUnread(unread));
1058
+ else setMessage({ ok: false, text: r.message });
1059
+ };
1060
+ const testAndSave = async () => {
1061
+ setBusy(true);
1062
+ setMessage({ ok: true, text: t.sTesting });
1063
+ const r = await settingsApi.testMail(locale(), account, password);
637
1064
  if (r.ok) {
638
- setPassword("");
639
- onDone(r.view);
1065
+ await save(r.unread);
640
1066
  } else {
1067
+ setBusy(false);
1068
+ setFailedTest(true);
641
1069
  setMessage({ ok: false, text: r.message });
642
1070
  }
643
1071
  };
644
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-add", children: [
645
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
646
- t.sProvider,
647
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("select", { value: account.preset, onChange: (e) => set({ preset: e.target.value }), children: view.presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: p.id, children: p.label }, p.id)) })
648
- ] }),
649
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: preset.supported ? "mishu-hint" : "mishu-hint mishu-danger", children: [
650
- preset.howTo,
651
- preset.supported ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AskAI, { topic: { kind: "mail-setup", preset: preset.id }, lang, locale, openChat: openChat2, compact: true }) : null
652
- ] }),
653
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
1072
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Modal, { title: existing ? t.mTitleEdit : t.mTitleAdd, onClose: () => onDone(), children: [
1073
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
654
1074
  t.sAddress,
655
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "email", value: account.address ?? "", autoComplete: "off", onChange: (e) => set({ address: e.target.value }) })
1075
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "email", value: account.address ?? "", autoComplete: "off", autoFocus: !existing, onChange: (e) => setAddress(e.target.value) })
656
1076
  ] }),
657
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-row", children: [
658
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
659
- t.sLabel,
660
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { value: account.label ?? "", onChange: (e) => set({ label: e.target.value }) })
661
- ] }),
662
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
663
- t.sUser,
664
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { value: account.user ?? "", placeholder: account.address ?? "", autoComplete: "off", onChange: (e) => set({ user: e.target.value }) })
665
- ] })
1077
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
1078
+ t.sProvider,
1079
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { value: account.preset, onChange: (e) => {
1080
+ setPicked(true);
1081
+ set({ preset: e.target.value });
1082
+ }, children: view.presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: p.id, children: p.label }, p.id)) })
1083
+ ] }),
1084
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `mishu-steps${preset.supported ? "" : " mishu-danger"}${failedTest ? " attention" : ""}`, children: [
1085
+ preset.steps.length === 0 ? preset.howTo : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ol", { children: preset.steps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { children: [
1086
+ step.text,
1087
+ step.link === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1088
+ " ",
1089
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => window.open(step.link.url, "_blank", "noopener"), children: step.link.label })
1090
+ ] })
1091
+ ] }, i)) }),
1092
+ preset.supported ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-steps-help", children: [
1093
+ t.mStepsHelp,
1094
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AskAI, { topic: { kind: "mail-setup", preset: preset.id }, lang, locale, openChat: openChat2, compact: true })
1095
+ ] }) : null
666
1096
  ] }),
667
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
1097
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
668
1098
  t.sPassword,
669
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "password", value: password, autoComplete: "new-password", placeholder: existing && view.secrets.available ? t.sPasswordKeep : "", onChange: (e) => {
1099
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "password", value: password, autoComplete: "new-password", placeholder: existing && view.secrets.available ? t.sPasswordKeep : "", onChange: (e) => {
670
1100
  setPassword(e.target.value);
671
1101
  setMessage(void 0);
1102
+ setFailedTest(false);
672
1103
  } })
673
1104
  ] }),
674
- view.secrets.available ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-hint mishu-warn", children: t.sPasswordTestOnly }),
675
- custom ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-row", children: [
676
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
1105
+ lengthOff ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint mishu-warn", children: t.mLength(preset.label, preset.passwordLength, typed.length) }) : null,
1106
+ view.secrets.available ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mishu-hint mishu-warn", children: t.sPasswordTestOnly }),
1107
+ custom ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-row", children: [
1108
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
677
1109
  t.sHost,
678
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { value: account.host ?? "", onChange: (e) => set({ host: e.target.value }) })
1110
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { value: account.host ?? "", placeholder: "imap.example.com", onChange: (e) => set({ host: e.target.value }) })
679
1111
  ] }),
680
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
1112
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
681
1113
  t.sPort,
682
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "number", value: account.port ?? 993, onChange: (e) => set({ port: Number(e.target.value) }) })
1114
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "number", value: account.port ?? 993, onChange: (e) => set({ port: Number(e.target.value) }) })
683
1115
  ] })
684
1116
  ] }) : null,
685
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-row", children: [
686
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
687
- t.sSince,
688
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "number", min: 1, max: 90, value: account.sinceDays ?? 14, onChange: (e) => set({ sinceDays: Number(e.target.value) }) })
1117
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { className: "mishu-more", children: [
1118
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("summary", { children: t.mMore }),
1119
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-row", children: [
1120
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
1121
+ t.sLabel,
1122
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { value: account.label ?? "", placeholder: account.address ?? "", onChange: (e) => set({ label: e.target.value }) })
1123
+ ] }),
1124
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
1125
+ t.sUser,
1126
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { value: account.user ?? "", placeholder: account.address ?? "", autoComplete: "off", onChange: (e) => set({ user: e.target.value }) })
1127
+ ] })
689
1128
  ] }),
690
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-check", children: [
691
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: account.enabled !== false, onChange: (e) => set({ enabled: e.target.checked }) }),
692
- t.sEnabled
1129
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-row", children: [
1130
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-field", children: [
1131
+ t.sSince,
1132
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "number", min: 1, max: 90, value: account.sinceDays ?? 14, onChange: (e) => set({ sinceDays: Number(e.target.value) }) })
1133
+ ] }),
1134
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "mishu-check", children: [
1135
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", checked: account.enabled !== false, onChange: (e) => set({ enabled: e.target.checked }) }),
1136
+ t.sEnabled
1137
+ ] })
693
1138
  ] })
694
1139
  ] }),
695
- message === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `mishu-preview ${message.ok ? "" : "mishu-danger"}`, children: [
1140
+ message === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `mishu-preview ${message.ok ? "" : "mishu-danger"}`, children: [
696
1141
  message.text,
697
- message.ok || !preset.supported ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AskAI, { topic: { kind: "mail-setup", preset: preset.id, error: message.text }, lang, locale, openChat: openChat2, compact: true })
1142
+ message.ok || !preset.supported ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AskAI, { topic: { kind: "mail-setup", preset: preset.id, error: message.text }, lang, locale, openChat: openChat2, compact: true })
698
1143
  ] }),
699
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-actions", children: [
700
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", disabled: busy || !preset.supported, onClick: () => {
701
- void test();
702
- }, children: t.sTest }),
703
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn primary", disabled: busy || !preset.supported, onClick: () => {
1144
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mishu-actions", children: [
1145
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn primary", disabled: busy || !preset.supported || (account.address ?? "") === "", onClick: () => {
1146
+ void testAndSave();
1147
+ }, children: busy ? t.sTesting : t.mTestSave }),
1148
+ failedTest ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", disabled: busy, onClick: () => {
1149
+ setBusy(true);
704
1150
  void save();
705
- }, children: t.sSave }),
706
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => onDone(), children: t.sCancel })
1151
+ }, children: t.mSaveAnyway }) : null,
1152
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => onDone(), children: t.sCancel })
707
1153
  ] })
708
1154
  ] });
709
1155
  }
710
1156
 
711
1157
  // src/client/Board.tsx
712
- var import_jsx_runtime3 = require("react/jsx-runtime");
1158
+ var import_jsx_runtime5 = require("react/jsx-runtime");
713
1159
  var SOURCE_KEY = "dsh-mishu.source";
714
1160
  function loadSource() {
715
1161
  try {
@@ -745,14 +1191,15 @@ function saveWidths(widths) {
745
1191
  }
746
1192
  }
747
1193
  function MishuBoard({ locale, openChat: openChat2 }) {
748
- const [board, setBoard] = (0, import_react3.useState)();
749
- const [error, setError] = (0, import_react3.useState)();
750
- const [busy, setBusy] = (0, import_react3.useState)(false);
751
- const [syncing, setSyncing] = (0, import_react3.useState)(false);
752
- const [dropOn, setDropOn] = (0, import_react3.useState)();
753
- const [widths, setWidths] = (0, import_react3.useState)(loadWidths);
754
- const [tab, setTab] = (0, import_react3.useState)("board");
755
- const [source, setSourceState] = (0, import_react3.useState)(loadSource);
1194
+ const [board, setBoard] = (0, import_react5.useState)();
1195
+ const [error, setError] = (0, import_react5.useState)();
1196
+ const [busy, setBusy] = (0, import_react5.useState)(false);
1197
+ const [syncing, setSyncing] = (0, import_react5.useState)(false);
1198
+ const [dropOn, setDropOn] = (0, import_react5.useState)();
1199
+ const [widths, setWidths] = (0, import_react5.useState)(loadWidths);
1200
+ const [tab, setTab] = (0, import_react5.useState)("board");
1201
+ const [openCard, setOpenCard] = (0, import_react5.useState)();
1202
+ const [source, setSourceState] = (0, import_react5.useState)(loadSource);
756
1203
  const setSource = (next) => {
757
1204
  setSourceState(next);
758
1205
  try {
@@ -760,11 +1207,11 @@ function MishuBoard({ locale, openChat: openChat2 }) {
760
1207
  } catch {
761
1208
  }
762
1209
  };
763
- const dragged = (0, import_react3.useRef)(void 0);
764
- const columnsRef = (0, import_react3.useRef)(null);
1210
+ const dragged = (0, import_react5.useRef)(void 0);
1211
+ const columnsRef = (0, import_react5.useRef)(null);
765
1212
  const lang = board?.lang ?? uiLang(locale());
766
1213
  const t = TEXT[lang];
767
- const take = (0, import_react3.useCallback)((result) => {
1214
+ const take = (0, import_react5.useCallback)((result) => {
768
1215
  if (result.ok) {
769
1216
  setBoard(result.board);
770
1217
  setError(void 0);
@@ -772,8 +1219,8 @@ function MishuBoard({ locale, openChat: openChat2 }) {
772
1219
  setError(result.message);
773
1220
  }
774
1221
  }, []);
775
- const load = (0, import_react3.useCallback)(() => mishuApi.board(locale()).then(take), [locale, take]);
776
- (0, import_react3.useEffect)(() => {
1222
+ const load = (0, import_react5.useCallback)(() => mishuApi.board(locale()).then(take), [locale, take]);
1223
+ (0, import_react5.useEffect)(() => {
777
1224
  void load();
778
1225
  const timer = setInterval(() => {
779
1226
  void load();
@@ -863,7 +1310,12 @@ function MishuBoard({ locale, openChat: openChat2 }) {
863
1310
  reopen: t.reopen,
864
1311
  stop: t.stop
865
1312
  };
866
- const card = (item, column) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1313
+ const cardKey = (item) => `${item.target}-${item.id}`;
1314
+ const toggleCard = (item) => {
1315
+ const key = cardKey(item);
1316
+ setOpenCard((current) => current === key ? void 0 : key);
1317
+ };
1318
+ const card = (item, column) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
867
1319
  Card,
868
1320
  {
869
1321
  item,
@@ -873,6 +1325,8 @@ function MishuBoard({ locale, openChat: openChat2 }) {
873
1325
  busy,
874
1326
  label,
875
1327
  openChat: openChat2,
1328
+ open: openCard === cardKey(item),
1329
+ onToggle: () => toggleCard(item),
876
1330
  onAct: (action) => {
877
1331
  void act(item, action);
878
1332
  },
@@ -884,15 +1338,15 @@ function MishuBoard({ locale, openChat: openChat2 }) {
884
1338
  setDropOn(void 0);
885
1339
  }
886
1340
  },
887
- `${item.target}-${item.id}`
1341
+ cardKey(item)
888
1342
  );
889
1343
  if (board === void 0) {
890
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-page", children: [
891
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-head", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-title", children: [
892
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { children: t.title }),
893
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RestartButton, { lang, locale })
1344
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-page", children: [
1345
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-head", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-title", children: [
1346
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { children: t.title }),
1347
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestartButton, { lang, locale })
894
1348
  ] }) }),
895
- error === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mishu-empty", children: t.loading }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-error", children: [
1349
+ error === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mishu-empty", children: t.loading }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-error", children: [
896
1350
  t.failed,
897
1351
  error
898
1352
  ] })
@@ -917,7 +1371,7 @@ function MishuBoard({ locale, openChat: openChat2 }) {
917
1371
  { id: "manual", label: t.sourceManual }
918
1372
  ];
919
1373
  const template = `minmax(0, ${widths[0]}fr) 10px minmax(0, ${widths[1]}fr) 10px minmax(0, ${widths[2]}fr)`;
920
- const divider = (index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1374
+ const divider = (index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
921
1375
  "div",
922
1376
  {
923
1377
  className: "mishu-divider",
@@ -928,133 +1382,135 @@ function MishuBoard({ locale, openChat: openChat2 }) {
928
1382
  onDoubleClick: resetWidths
929
1383
  }
930
1384
  );
931
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-page", "data-mishu-board": "", children: [
932
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-head", children: [
933
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-title", children: [
934
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { children: t.title }),
935
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-tabs", role: "tablist", children: [
936
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === "board", className: tab === "board" ? "active" : "", onClick: () => setTab("board"), children: t.tabBoard }),
937
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === "settings", className: tab === "settings" ? "active" : "", onClick: () => setTab("settings"), children: t.tabSettings })
1385
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-page", "data-mishu-board": "", children: [
1386
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-head", children: [
1387
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-title", children: [
1388
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { children: t.title }),
1389
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-tabs", role: "tablist", children: [
1390
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === "board", className: tab === "board" ? "active" : "", onClick: () => setTab("board"), children: t.tabBoard }),
1391
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === "settings", className: tab === "settings" ? "active" : "", onClick: () => setTab("settings"), children: t.tabSettings })
938
1392
  ] }),
939
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RestartButton, { lang, locale })
1393
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestartButton, { lang, locale })
940
1394
  ] }),
941
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-sources", children: [
942
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
943
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: `mishu-dot ${board.sources.github.state}` }),
1395
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-sources", children: [
1396
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
1397
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("i", { className: `mishu-dot ${board.sources.github.state}` }),
944
1398
  board.sources.github.text,
945
- board.sources.github.state === "error" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: { kind: "github-error" }, lang, locale, openChat: openChat2, compact: true }) : null
1399
+ board.sources.github.state === "error" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AskAI, { topic: { kind: "github-error" }, lang, locale, openChat: openChat2, compact: true }) : null
946
1400
  ] }),
947
- board.sources.mail.map((m) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
948
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: `mishu-dot ${m.state}` }),
1401
+ board.sources.mail.map((m) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
1402
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("i", { className: `mishu-dot ${m.state}` }),
949
1403
  m.text,
950
- m.state === "error" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: { kind: "mail-error", accountId: m.id }, lang, locale, openChat: openChat2, compact: true }) : null
1404
+ m.state === "error" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AskAI, { topic: { kind: "mail-error", accountId: m.id }, lang, locale, openChat: openChat2, compact: true }) : null
951
1405
  ] }, m.id)),
952
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn", disabled: syncing, onClick: () => {
1406
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "mishu-btn", disabled: syncing, onClick: () => {
953
1407
  void sync();
954
1408
  }, children: syncing ? t.syncing : t.sync })
955
1409
  ] })
956
1410
  ] }),
957
- tab === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MishuSettings, { lang, locale, openChat: openChat2 }) : null,
958
- tab === "settings" ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
959
- error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-error", children: [
1411
+ tab === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MishuSettings, { lang, locale, openChat: openChat2 }) : null,
1412
+ tab === "settings" ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1413
+ error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-error", children: [
960
1414
  t.failed,
961
1415
  error
962
1416
  ] }),
963
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "mishu-summary", children: [
1417
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "mishu-summary", children: [
964
1418
  board.summary,
965
1419
  " ",
966
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: { kind: "summary" }, lang, locale, openChat: openChat2, compact: true })
1420
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AskAI, { topic: { kind: "summary" }, lang, locale, openChat: openChat2, compact: true })
967
1421
  ] }),
968
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metrics", children: [
969
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric", children: [
970
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-label", children: t.metricHandle }),
971
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric-value", children: [
1422
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-metrics", children: [
1423
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-metric", children: [
1424
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-metric-label", children: t.metricHandle }),
1425
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-metric-value", children: [
972
1426
  metrics.handle,
973
- metrics.overdue > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("small", { className: "mishu-danger", children: [
1427
+ metrics.overdue > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("small", { className: "mishu-danger", children: [
974
1428
  metrics.overdue,
975
1429
  " ",
976
1430
  t.metricOverdue
977
1431
  ] }) : null
978
1432
  ] })
979
1433
  ] }),
980
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric", children: [
981
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-label", children: t.metricSoon }),
982
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-value", children: metrics.dueSoon })
1434
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-metric", children: [
1435
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-metric-label", children: t.metricSoon }),
1436
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-metric-value", children: metrics.dueSoon })
983
1437
  ] }),
984
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric", children: [
985
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-label", children: t.metricClosed }),
986
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-value", children: metrics.closedToday })
1438
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-metric", children: [
1439
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-metric-label", children: t.metricClosed }),
1440
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-metric-value", children: metrics.closedToday })
987
1441
  ] }),
988
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric", children: [
989
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-label", children: t.metricShadow }),
990
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric-value", children: [
1442
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-metric", children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-metric-label", children: t.metricShadow }),
1444
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-metric-value", children: [
991
1445
  metrics.shadow,
992
- board.markRead === "shadow" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { className: "mishu-warn", children: t.shadowTag }) : null
1446
+ board.markRead === "shadow" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("small", { className: "mishu-warn", children: t.shadowTag }) : null
993
1447
  ] })
994
1448
  ] })
995
1449
  ] }),
996
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-filters", role: "group", children: sources.map((s) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", "aria-pressed": source === s.id, className: `mishu-chip ${source === s.id ? "active" : ""}`, onClick: () => setSource(s.id), children: [
1450
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-filters", role: "group", children: sources.map((s) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { type: "button", "aria-pressed": source === s.id, className: `mishu-chip ${source === s.id ? "active" : ""}`, onClick: () => setSource(s.id), children: [
997
1451
  s.label,
998
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: count(s.id) })
1452
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: count(s.id) })
999
1453
  ] }, s.id)) }),
1000
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-columns", ref: columnsRef, style: { ["--mishu-cols"]: template }, children: [
1001
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: `mishu-col ${dropOn === "handle" ? "drop" : ""}`, ...dropProps("handle"), "aria-label": t.handle, children: [
1002
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-col-head", children: [
1454
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-columns", ref: columnsRef, style: { ["--mishu-cols"]: template }, children: [
1455
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: `mishu-col ${dropOn === "handle" ? "drop" : ""}`, ...dropProps("handle"), "aria-label": t.handle, children: [
1456
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-col-head", children: [
1003
1457
  t.handle,
1004
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: columns.handle.length })
1458
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: columns.handle.length })
1005
1459
  ] }),
1006
- columns.handle.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-empty", children: t.emptyHandle }) : columns.handle.map((item) => card(item, "handle"))
1460
+ columns.handle.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-empty", children: t.emptyHandle }) : columns.handle.map((item) => card(item, "handle"))
1007
1461
  ] }),
1008
1462
  divider(0),
1009
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: `mishu-col ${dropOn === "remember" ? "drop" : ""}`, ...dropProps("remember"), "aria-label": t.remember, title: t.dropRemember, children: [
1010
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-col-head", children: [
1463
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: `mishu-col ${dropOn === "remember" ? "drop" : ""}`, ...dropProps("remember"), "aria-label": t.remember, title: t.dropRemember, children: [
1464
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-col-head", children: [
1011
1465
  t.remember,
1012
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: columns.remember.length })
1466
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: columns.remember.length })
1013
1467
  ] }),
1014
- columns.remember.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-empty", children: t.emptyRemember }) : columns.remember.map((item) => card(item, "remember")),
1015
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AddForm, { lang, locale, onAdded: (next) => setBoard(next) })
1468
+ columns.remember.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-empty", children: t.emptyRemember }) : columns.remember.map((item) => card(item, "remember")),
1469
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AddForm, { lang, locale, onAdded: (next) => setBoard(next) })
1016
1470
  ] }),
1017
1471
  divider(1),
1018
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: `mishu-col ${dropOn === "done" ? "drop" : ""}`, ...dropProps("done"), "aria-label": t.done, title: t.dropDone, children: [
1019
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-col-head", children: [
1472
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: `mishu-col ${dropOn === "done" ? "drop" : ""}`, ...dropProps("done"), "aria-label": t.done, title: t.dropDone, children: [
1473
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-col-head", children: [
1020
1474
  t.done,
1021
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: columns.done.total })
1475
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: columns.done.total })
1022
1476
  ] }),
1023
- columns.done.groups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-empty", children: t.emptyDone }) : columns.done.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("details", { className: "mishu-group", children: [
1024
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("summary", { children: [
1477
+ columns.done.groups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-empty", children: t.emptyDone }) : columns.done.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("details", { className: "mishu-group", children: [
1478
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("summary", { children: [
1025
1479
  group.label,
1026
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: group.items.length })
1480
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: group.items.length })
1027
1481
  ] }),
1028
1482
  group.items.map((item) => card(item, "done"))
1029
1483
  ] }, group.reason)),
1030
- columns.done.total > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mishu-hint", children: t.doneHint }) : null,
1031
- metrics.shadow > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-banner", children: t.shadow(metrics.shadow) }) : null
1484
+ columns.done.total > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mishu-hint", children: t.doneHint }) : null,
1485
+ metrics.shadow > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-banner", children: t.shadow(metrics.shadow) }) : null
1032
1486
  ] })
1033
1487
  ] })
1034
1488
  ] })
1035
1489
  ] });
1036
1490
  }
1037
- function Card({ item, column, lang, locale, busy, label, openChat: openChat2, onAct, onDragStart, onDragEnd }) {
1491
+ function Card({ item, column, lang, locale, busy, label, openChat: openChat2, open, onToggle, onAct, onDragStart, onDragEnd }) {
1038
1492
  const t = TEXT[lang];
1039
- const [open, setOpen] = (0, import_react3.useState)(false);
1040
- const [detail, setDetail] = (0, import_react3.useState)();
1493
+ const [detail, setDetail] = (0, import_react5.useState)();
1041
1494
  const explain = item.explain;
1042
1495
  const compact = column === "done";
1043
- const toggle = () => {
1044
- const next = !open;
1045
- setOpen(next);
1046
- if (next && item.target === "matter") {
1047
- void mishuApi.detail(locale(), item.id).then((r) => setDetail(r.ok ? r.detail : { error: r.message }));
1048
- }
1049
- };
1050
- const row = (name, text, className = "") => text === null || text === void 0 || text === "" ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `mishu-row-explain ${className}`, children: [
1051
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mishu-row-label", children: name }),
1052
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: text })
1496
+ (0, import_react5.useEffect)(() => {
1497
+ if (!open || item.target !== "matter" || detail !== void 0) return;
1498
+ let alive = true;
1499
+ void mishuApi.detail(locale(), item.id).then((r) => {
1500
+ if (alive) setDetail(r.ok ? r.detail : { error: r.message });
1501
+ });
1502
+ return () => {
1503
+ alive = false;
1504
+ };
1505
+ }, [open, item.target, item.id, detail, locale]);
1506
+ const row = (name, text, className = "") => text === null || text === void 0 || text === "" ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `mishu-row-explain ${className}`, children: [
1507
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "mishu-row-label", children: name }),
1508
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: text })
1053
1509
  ] });
1054
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1510
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1055
1511
  "div",
1056
1512
  {
1057
- className: `mishu-card ${item.tone === "normal" ? "" : item.tone}`,
1513
+ className: `mishu-card ${open ? "open" : ""} ${item.tone === "normal" ? "" : item.tone}`,
1058
1514
  draggable: item.target === "matter" && column !== "done",
1059
1515
  onDragStart: (event) => {
1060
1516
  event.dataTransfer.effectAllowed = "move";
@@ -1062,48 +1518,68 @@ function Card({ item, column, lang, locale, busy, label, openChat: openChat2, on
1062
1518
  },
1063
1519
  onDragEnd,
1064
1520
  children: [
1065
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-card-title", children: item.title }),
1066
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-card-meta", children: item.meta }),
1067
- !compact && explain !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-explain", children: [
1068
- row(t.what, explain.what),
1069
- row(column === "remember" ? t.whyKeep : t.why, explain.why),
1070
- row(t.fix, explain.fix, "mishu-fix")
1071
- ] }) : null,
1072
- !compact && explain === null && item.note !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-card-note", children: item.note }) : null,
1073
- open ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-detail", children: detail === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-empty", children: t.loading }) : "error" in detail ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-danger", children: detail.error }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1074
- compact && detail.explain !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1075
- row(t.what, detail.explain.what),
1076
- row(t.whyDone, detail.explain.why)
1077
- ] }) : null,
1078
- detail.did.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-row-explain", children: [
1079
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mishu-row-label", children: t.did }),
1080
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: detail.did.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: line }, i)) })
1081
- ] }) : null,
1082
- detail.messages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-messages", children: [
1083
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-row-label", children: t.messages(detail.messages.length) }),
1084
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { children: detail.messages.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
1085
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: m.at }),
1086
- " ",
1087
- m.title
1088
- ] }, i)) })
1089
- ] }) : null
1090
- ] }) }) : null,
1091
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-actions", children: [
1092
- column === "handle" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: item.target === "series" ? { kind: "series", id: item.id } : { kind: "matter", id: item.id }, lang, locale, openChat: openChat2 }) : null,
1093
- item.actions.map((action, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1094
- "button",
1095
- {
1096
- type: "button",
1097
- className: `mishu-btn ${index === 0 ? "primary" : ""}`,
1098
- disabled: busy && action !== "open",
1099
- onClick: () => onAct(action),
1100
- children: action === "open" && explain?.link !== void 0 ? explain.link.label : label[action]
1521
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1522
+ "div",
1523
+ {
1524
+ className: "mishu-card-head",
1525
+ role: "button",
1526
+ tabIndex: 0,
1527
+ "aria-expanded": open,
1528
+ title: open ? t.hide : compact ? t.explainDone : t.detail,
1529
+ onClick: onToggle,
1530
+ onKeyDown: (event) => {
1531
+ if (event.key !== "Enter" && event.key !== " ") return;
1532
+ event.preventDefault();
1533
+ onToggle();
1101
1534
  },
1102
- action
1103
- )),
1104
- item.target === "matter" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", "aria-expanded": open, onClick: toggle, children: open ? t.hide : compact ? t.explainDone : t.detail }) : null,
1105
- column === "handle" ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: item.target === "series" ? { kind: "series", id: item.id } : { kind: "matter", id: item.id }, lang, locale, openChat: openChat2, compact: true })
1106
- ] })
1535
+ children: [
1536
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "mishu-caret", "aria-hidden": "true" }),
1537
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "mishu-card-title", children: item.title })
1538
+ ]
1539
+ }
1540
+ ),
1541
+ open ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-card-body", children: [
1542
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-card-meta", children: item.meta }),
1543
+ !compact && explain !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-explain", children: [
1544
+ row(t.what, explain.what),
1545
+ row(column === "remember" ? t.whyKeep : t.why, explain.why),
1546
+ row(t.fix, explain.fix, "mishu-fix")
1547
+ ] }) : null,
1548
+ !compact && explain === null && item.note !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-card-note", children: item.note }) : null,
1549
+ item.target === "matter" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-detail", children: detail === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-empty", children: t.loading }) : "error" in detail ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-danger", children: detail.error }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1550
+ compact && detail.explain !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1551
+ row(t.what, detail.explain.what),
1552
+ row(t.whyDone, detail.explain.why)
1553
+ ] }) : null,
1554
+ detail.did.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-row-explain", children: [
1555
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "mishu-row-label", children: t.did }),
1556
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: detail.did.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: line }, i)) })
1557
+ ] }) : null,
1558
+ detail.messages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-messages", children: [
1559
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mishu-row-label", children: t.messages(detail.messages.length) }),
1560
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { children: detail.messages.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { children: [
1561
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: m.at }),
1562
+ " ",
1563
+ m.title
1564
+ ] }, i)) })
1565
+ ] }) : null
1566
+ ] }) }) : null,
1567
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-actions", children: [
1568
+ column === "handle" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AskAI, { topic: item.target === "series" ? { kind: "series", id: item.id } : { kind: "matter", id: item.id }, lang, locale, openChat: openChat2 }) : null,
1569
+ item.actions.map((action, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1570
+ "button",
1571
+ {
1572
+ type: "button",
1573
+ className: `mishu-btn ${index === 0 ? "primary" : ""}`,
1574
+ disabled: busy && action !== "open",
1575
+ onClick: () => onAct(action),
1576
+ children: action === "open" && explain?.link !== void 0 ? explain.link.label : label[action]
1577
+ },
1578
+ action
1579
+ )),
1580
+ column === "handle" ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AskAI, { topic: item.target === "series" ? { kind: "series", id: item.id } : { kind: "matter", id: item.id }, lang, locale, openChat: openChat2, compact: true })
1581
+ ] })
1582
+ ] }) : null
1107
1583
  ]
1108
1584
  }
1109
1585
  );
@@ -1111,11 +1587,11 @@ function Card({ item, column, lang, locale, busy, label, openChat: openChat2, on
1111
1587
  var EMPTY = { title: "", due: "", repeat: "none", category: "other", note: "" };
1112
1588
  function AddForm({ lang, locale, onAdded }) {
1113
1589
  const t = TEXT[lang];
1114
- const [open, setOpen] = (0, import_react3.useState)(false);
1115
- const [item, setItem] = (0, import_react3.useState)(EMPTY);
1116
- const [preview, setPreview] = (0, import_react3.useState)();
1117
- const [saving, setSaving] = (0, import_react3.useState)(false);
1118
- (0, import_react3.useEffect)(() => {
1590
+ const [open, setOpen] = (0, import_react5.useState)(false);
1591
+ const [item, setItem] = (0, import_react5.useState)(EMPTY);
1592
+ const [preview, setPreview] = (0, import_react5.useState)();
1593
+ const [saving, setSaving] = (0, import_react5.useState)(false);
1594
+ (0, import_react5.useEffect)(() => {
1119
1595
  if (!open || item.title.trim() === "" || item.due === "") {
1120
1596
  setPreview(void 0);
1121
1597
  return;
@@ -1126,7 +1602,7 @@ function AddForm({ lang, locale, onAdded }) {
1126
1602
  return () => clearTimeout(timer);
1127
1603
  }, [open, item, locale]);
1128
1604
  if (!open) {
1129
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", className: "mishu-btn mishu-add-toggle", onClick: () => setOpen(true), children: [
1605
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { type: "button", className: "mishu-btn mishu-add-toggle", onClick: () => setOpen(true), children: [
1130
1606
  "+ ",
1131
1607
  t.add
1132
1608
  ] });
@@ -1144,49 +1620,49 @@ function AddForm({ lang, locale, onAdded }) {
1144
1620
  setPreview({ ok: false, text: result.message });
1145
1621
  }
1146
1622
  };
1147
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-add", children: [
1148
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1623
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-add", children: [
1624
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "mishu-field", children: [
1149
1625
  t.addTitle,
1150
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: item.title, placeholder: t.addTitlePlaceholder, onChange: set("title") })
1626
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { value: item.title, placeholder: t.addTitlePlaceholder, onChange: set("title") })
1151
1627
  ] }),
1152
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-row", children: [
1153
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1628
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-row", children: [
1629
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "mishu-field", children: [
1154
1630
  t.addDue,
1155
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "date", value: item.due, onChange: set("due") })
1631
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "date", value: item.due, onChange: set("due") })
1156
1632
  ] }),
1157
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1633
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "mishu-field", children: [
1158
1634
  t.addRepeat,
1159
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { value: item.repeat, onChange: set("repeat"), children: Object.entries(t.repeats).map(([value, text]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value, children: text }, value)) })
1635
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { value: item.repeat, onChange: set("repeat"), children: Object.entries(t.repeats).map(([value, text]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value, children: text }, value)) })
1160
1636
  ] })
1161
1637
  ] }),
1162
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-row", children: [
1163
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1638
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-row", children: [
1639
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "mishu-field", children: [
1164
1640
  t.addCategory,
1165
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { value: item.category, onChange: set("category"), children: Object.entries(t.categories).map(([value, text]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value, children: text }, value)) })
1641
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { value: item.category, onChange: set("category"), children: Object.entries(t.categories).map(([value, text]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value, children: text }, value)) })
1166
1642
  ] }),
1167
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1643
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "mishu-field", children: [
1168
1644
  t.addNote,
1169
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: item.note, onChange: set("note") })
1645
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { value: item.note, onChange: set("note") })
1170
1646
  ] })
1171
1647
  ] }),
1172
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `mishu-preview ${preview?.ok === false ? "mishu-danger" : ""}`, children: preview?.text ?? t.previewHint }),
1173
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-actions", children: [
1174
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn primary", disabled: saving || preview?.ok !== true, onClick: () => {
1648
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `mishu-preview ${preview?.ok === false ? "mishu-danger" : ""}`, children: preview?.text ?? t.previewHint }),
1649
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mishu-actions", children: [
1650
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "mishu-btn primary", disabled: saving || preview?.ok !== true, onClick: () => {
1175
1651
  void save();
1176
1652
  }, children: t.save }),
1177
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => {
1653
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => {
1178
1654
  setOpen(false);
1179
1655
  setItem(EMPTY);
1180
1656
  }, children: t.cancel })
1181
1657
  ] }),
1182
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mishu-hint", children: t.chatHint })
1658
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mishu-hint", children: t.chatHint })
1183
1659
  ] });
1184
1660
  }
1185
1661
  function RestartButton({ lang, locale }) {
1186
1662
  const t = TEXT[lang];
1187
- const [why, setWhy] = (0, import_react3.useState)();
1188
- const [status, setStatus] = (0, import_react3.useState)();
1189
- (0, import_react3.useEffect)(() => {
1663
+ const [why, setWhy] = (0, import_react5.useState)();
1664
+ const [status, setStatus] = (0, import_react5.useState)();
1665
+ (0, import_react5.useEffect)(() => {
1190
1666
  void restartApi.info(locale()).then((r) => {
1191
1667
  if (r.ok && !r.supported) setWhy(r.why);
1192
1668
  });
@@ -1199,16 +1675,35 @@ function RestartButton({ lang, locale }) {
1199
1675
  if (!window.confirm(t.restartConfirm)) return;
1200
1676
  setStatus({ ok: true, text: t.restarting });
1201
1677
  const r = await restartApi.restart(locale());
1202
- if (!r.ok) setStatus({ ok: false, text: r.message });
1678
+ if (!r.ok) {
1679
+ setStatus({ ok: false, text: r.message });
1680
+ } else if (r.reconnect === true) {
1681
+ setStatus({ ok: true, text: t.restartingWeb });
1682
+ setStatus({ ok: true, text: await reconnect(locale) ? t.restartReloading : t.restartSlow });
1683
+ }
1203
1684
  };
1204
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1205
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn", title: why === void 0 ? t.restartTip : `${t.restartTip}
1685
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1686
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "mishu-btn", title: why === void 0 ? t.restartTip : `${t.restartTip}
1206
1687
  ${why}`, disabled: status?.ok === true, onClick: () => {
1207
1688
  void restart();
1208
1689
  }, children: t.restart }),
1209
- status === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: status.ok ? "mishu-hint" : "mishu-hint mishu-warn", children: status.text })
1690
+ status === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: status.ok ? "mishu-hint" : "mishu-hint mishu-warn", children: status.text })
1210
1691
  ] });
1211
1692
  }
1693
+ async function reconnect(locale) {
1694
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1695
+ let wentDown = false;
1696
+ for (const until = Date.now() + 12e4; Date.now() < until; ) {
1697
+ await sleep(1500);
1698
+ const r = await restartApi.info(locale());
1699
+ if (!r.ok) wentDown = true;
1700
+ else if (wentDown) {
1701
+ window.location.reload();
1702
+ return true;
1703
+ }
1704
+ }
1705
+ return false;
1706
+ }
1212
1707
 
1213
1708
  // src/client/styles.ts
1214
1709
  var v = (name, fallback) => `var(--dsw-alias-${name}, ${fallback})`;
@@ -1224,6 +1719,7 @@ var DANGER = v("state-error-primary", "#d14343");
1224
1719
  var WARN = v("state-warn-primary", "#b7791f");
1225
1720
  var OK = v("state-success-primary", "#2f855a");
1226
1721
  var ACCENT = v("brand-primary", "#4d6bfe");
1722
+ var SURFACE = v("bg-layer-0", v("bg-base", "Canvas"));
1227
1723
  var STYLES = `
1228
1724
  .mishu-page{box-sizing:border-box;height:100%;overflow:auto;padding:20px 24px 32px;color:${TEXT2};font-size:13px;line-height:1.5}
1229
1725
  .mishu-page *{box-sizing:border-box}
@@ -1248,10 +1744,17 @@ var STYLES = `
1248
1744
  .mishu-col-head{display:flex;justify-content:space-between;align-items:baseline;font-weight:600;margin:2px 4px 8px}
1249
1745
  .mishu-col-head span{color:${MUTED};font-weight:400}
1250
1746
  .mishu-empty{color:${FAINT};padding:8px 4px}
1251
- .mishu-card{background:${LAYER2};border:1px solid ${LINE};border-radius:8px;padding:8px 10px;margin-bottom:8px}
1747
+ .mishu-card{background:${LAYER2};border:1px solid ${LINE};border-radius:8px;padding:6px 8px;margin-bottom:8px}
1252
1748
  .mishu-card[draggable=true]{cursor:grab}
1253
1749
  .mishu-card.danger{border-color:${DANGER}}
1254
- .mishu-card-title{font-weight:600;word-break:break-word}
1750
+ .mishu-card.open{padding-bottom:8px}
1751
+ .mishu-card-head{display:flex;align-items:flex-start;gap:6px;width:100%;font:inherit;font-size:13px;text-align:left;color:inherit;background:transparent;border:0;border-radius:6px;padding:3px 4px;cursor:pointer}
1752
+ .mishu-card-head:hover{background:${HOVER}}
1753
+ .mishu-card-head:focus-visible{outline:2px solid ${ACCENT};outline-offset:1px}
1754
+ .mishu-caret{flex:0 0 auto;width:0;height:0;margin-top:6px;border-left:4px solid ${FAINT};border-top:4px solid transparent;border-bottom:4px solid transparent;transition:transform .12s}
1755
+ .mishu-card.open .mishu-caret{transform:rotate(90deg);border-left-color:${ACCENT}}
1756
+ .mishu-card-body{padding:0 4px}
1757
+ .mishu-card-title{font-weight:600;word-break:break-word;min-width:0;flex:1 1 auto}
1255
1758
  .mishu-card-meta{font-size:12px;color:${MUTED};margin-top:2px}
1256
1759
  .mishu-card.danger .mishu-card-meta{color:${DANGER}}
1257
1760
  .mishu-card.warn .mishu-card-meta{color:${WARN}}
@@ -1284,6 +1787,22 @@ var STYLES = `
1284
1787
  .mishu-row{display:grid;grid-template-columns:1fr 1fr;gap:6px}
1285
1788
  .mishu-preview{white-space:pre-wrap;font-size:12px;background:${LAYER2};border-radius:6px;padding:6px 8px;margin:4px 0 6px;color:${TEXT2}}
1286
1789
  .mishu-preview.mishu-danger{color:${DANGER}}
1790
+ .mishu-modal-backdrop{position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;padding:16px}
1791
+ .mishu-modal{background:${SURFACE};color:${TEXT2};border:1px solid ${LINE_STRONG};border-radius:12px;box-shadow:0 12px 40px rgba(0,0,0,.35);width:min(480px,100%);max-height:calc(100vh - 32px);overflow:auto;padding:14px 16px 16px;font-size:13px;line-height:1.5}
1792
+ .mishu-modal *{box-sizing:border-box}
1793
+ .mishu-modal-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
1794
+ .mishu-modal-head h2{font-size:15px;font-weight:600;margin:0}
1795
+ .mishu-steps{font-size:12px;color:${MUTED};background:${LAYER1};border-radius:8px;padding:8px 10px;margin:2px 0 10px;line-height:1.6}
1796
+ .mishu-steps ol{margin:0;padding-left:20px}
1797
+ .mishu-steps li{margin:2px 0 6px}
1798
+ .mishu-steps li .mishu-btn{margin-left:4px;vertical-align:baseline}
1799
+ .mishu-steps.attention{outline:2px solid ${ACCENT};outline-offset:1px;color:${TEXT2}}
1800
+ .mishu-steps-help{display:flex;align-items:center;gap:6px;margin-top:4px}
1801
+ .mishu-steps.mishu-danger{color:${DANGER}}
1802
+ .mishu-more{margin:4px 0 8px;font-size:12px;color:${MUTED}}
1803
+ .mishu-more summary{cursor:pointer;margin-bottom:6px}
1804
+ .mishu-code-row{display:flex;align-items:center;gap:10px;margin:2px 0 10px}
1805
+ .mishu-code{font:600 26px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:3px;user-select:all;padding:4px 10px;border-radius:8px;background:${LAYER2}}
1287
1806
  .mishu-add>.mishu-hint{margin-bottom:8px}
1288
1807
  .mishu-title{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
1289
1808
  .mishu-tabs{display:flex;gap:2px;background:${LAYER1};border-radius:8px;padding:2px}
@@ -1297,6 +1816,9 @@ var STYLES = `
1297
1816
  .mishu-settings{display:flex;flex-direction:column;gap:12px;max-width:760px}
1298
1817
  .mishu-panel{background:${LAYER1};border-radius:12px;padding:12px 14px}
1299
1818
  .mishu-panel h2{font-size:14px;font-weight:600;margin:0 0 8px}
1819
+ .mishu-panel h3{font-size:13px;font-weight:600;margin:14px 0 6px}
1820
+ .mishu-notify-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:6px}
1821
+ .mishu-notify-row input[type=time]{font:inherit;font-size:13px;color:${TEXT2};background:${LAYER2};border:1px solid ${LINE_STRONG};border-radius:6px;padding:2px 6px}
1300
1822
  .mishu-grid5{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:6px;margin-bottom:8px}
1301
1823
  .mishu-check{display:flex;align-items:center;gap:6px;font-size:13px;margin:4px 0 8px;color:${TEXT2}}
1302
1824
  .mishu-ok{padding:6px 10px;border-radius:8px;border:1px solid ${OK};color:${OK};font-size:12px}
@@ -1312,18 +1834,18 @@ var STYLES = `
1312
1834
  `;
1313
1835
 
1314
1836
  // src/client/index.tsx
1315
- var import_jsx_runtime4 = require("react/jsx-runtime");
1837
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1316
1838
  var PACKAGE_NAME = "dsh-mishu";
1317
1839
  var inject = ["slots", "locale"];
1318
1840
  var PANEL_ID = "dsh-mishu";
1319
1841
  var NS = "dsh-mishu";
1320
1842
  var openChat;
1321
1843
  function MishuIcon({ size }) {
1322
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1323
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "5", y: "4", width: "14", height: "17", rx: "2" }),
1324
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 4h6v3H9z" }),
1325
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 12l2 2 4-4" }),
1326
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 17h6" })
1844
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1845
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: "5", y: "4", width: "14", height: "17", rx: "2" }),
1846
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M9 4h6v3H9z" }),
1847
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M9 12l2 2 4-4" }),
1848
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M9 17h6" })
1327
1849
  ] });
1328
1850
  }
1329
1851
  function apply(ctx) {